https://xueyuanjun.com/post/9178.html
上一篇我们简单演示了 Laravel 5.5 中 RESTful API 的构建、认证和测试,本教程将在上一篇教程的基础上进行升华,我们将结合 Laravel 和 Vue 单页面应用(SPA),在此双剑合壁的基础上引入 jwt-auth 实现 API 认证,由于 Laravel 集成了对 Vue 的支持,所以在 Laravel 应用中使用 Vue 也是如鱼得水,非常顺畅,整篇教程涉及到的工具包括:
- Node.js
- Laravel 5.5
- jwt-auth
- NPM
- Vue.js 2.x
- Vue-router
- Vue-axios
- @websanova/vue-auth
初始化前端
我们将会在上一篇创建应用的基础上进行开发。
首先在项目根目录下运行以下命令安装前端依赖:
1
npm install
然后安装一些必要的 Vue 组件:
1
npm install --save-dev vue-axios vue-router vue-loader vue-template-compiler
接下来我们来创建应用所需的 Vue 模板和视图。
在 resources/assets/js
目录下新建 App.vue
:
1
<template>
2
<div class="panel panel-default">
3
<div class="panel-heading">
4
<nav>
5
<ul class="list-inline">
6
<li>
7
<router-link :to="{ name: 'home' }">首页</router-link>
8
</li>
9
<li class="pull-right">
10
<router-link :to="{ name: 'login' }">登录</router-link>
11
</li>
12
<li class="pull-right">
13
<router-link :to="{ name: 'register' }">注册</router-link>
14
</li>
15
</ul>
16
</nav>
17
</div>
18
<div class="panel-body">
19
<router-view></router-view>
20
</div>
21
</div>
22
</template>
在 resources/assets/js/components
目录下新增 Home.vue
:
1
<template>
2
<h1>Laravel 5 Vue SPA 认证</h1>
3
</template>
替换 resouces/assets/js/app.js
内容如下:
1
import Vue from 'vue';
2
import VueRouter from 'vue-router';
3
import App from './App.vue';
4
import Home from './components/Home.vue';
5
6
Vue.use(VueRouter);
7
8
const router = new VueRouter({
9
routes: [
10
{
11
path: '/',
12
name: 'home',
13
component: Home
14
},
15
]
16
});
17
18
new Vue({
19
el: '#app',
20
router: router,
21
render: app => app(App)
22
});
替换 resources/views/welcome.blade.php
内容如下:
1
<!DOCTYPE html>
2
<html>
3
<head>
4
<meta charset="utf-8">
5
<meta name="csrf-token" content="{{ csrf_token() }}">
6
<title>Laravel</title>
7
8
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
9
10
</head>
11
<body>
12
<div class="container">
13
<div id="app"></div>
14
</div>
15
<script src="/js/app.js"></script>
16
</body>
17
</html>
最后在项目根目录下运行 npm run watch
,就可以在浏览器中通过 http://apidemo.test
访问新首页了:
创建 Vue 组件
接下来我们来创建需要的 Vue 组件。
在 resources/assets/js/components
目录下新建 Register.vue
:
1
<template>
2
<div>
3
<div class="alert alert-danger" v-if="error && !success">
4
<p>出错了,很遗憾,未能完成注册</p>
5
</div>
6
<div class="alert alert-success" v-if="success">
7
<p>注册完成,你现在可以<router-link :to="{name:'login'}">登录</router-link>了</p>
8
</div>
9
<form autocomplete="off" @submit.prevent="register" v-if="!success" method="post">
10
<div class="form-group" v-bind:class="{ 'has-error': error && errors.name }">
11
<label for="name">用户名</label>
12
<input type="text" id="name" class="form-control" v-model="name" required>
13
<span class="help-block" v-if="error && errors.name">{{ errors.name }}</span>
14
</div>
15
<div class="form-group" v-bind:class="{ 'has-error': error && errors.email }">
16
<label for="email">邮箱</label>
17
<input type="email" id="email" class="form-control" placeholder="user@example.com" v-model="email" required>
18
<span class="help-block" v-if="error && errors.email">{{ errors.email }}</span>
19
</div>
20
<div class="form-group" v-bind:class="{ 'has-error': error && errors.password }">
21
<label for="password">密码</label>
22
<input type="password" id="password" class="form-control" v-model="password" required>
23
<span class="help-block" v-if="error && errors.password">{{ errors.password }}</span>
24
</div>
25
<button type="submit" class="btn btn-default">提交</button>
26
</form>
27
</div>
28
</template>
在同一目录下创建 Login.vue
:
1
<template>
2
<div>
3
<div class="alert alert-danger" v-if="error">
4
<p>出错了,请检查邮箱/密码是否正确</p>
5
</div>
6
<form autocomplete="off" @submit.prevent="login" method="post">
7
<div class="form-group">
8
<label for="email">邮箱</label>
9
<input type="email" id="email" class="form-control" placeholder="user@example.com" v-model="email" required>
10
</div>
11
<div class="form-group">
12
<label for="password">密码</label>
13
<input type="password" id="password" class="form-control" v-model="password" required>
14
</div>
15
<button type="submit" class="btn btn-default">登录</button>
16
</form>
17
</div>
18
</template>
最后在该目录下新建 Dashboard.vue
:
1
<template>
2
<h1>Laravel 5 – 酷炫的后台</h1>
3
</template>
编辑 resources/assets/js/app.js
文件内容如下:
1
import Vue from 'vue';
2
import VueRouter from 'vue-router';
3
import axios from 'axios';
4
import VueAxios from 'vue-axios';
5
import App from './App.vue';
6
import Dashboard from './components/Dashboard.vue';
7
import Home from './components/Home.vue';
8
import Register from './components/Register.vue';
9
import Login from './components/Login.vue';
10
Vue.use(VueRouter);
11
Vue.use(VueAxios, axios);
12
axios.defaults.baseURL = 'http://apidemo.test/api';
13
const router = new VueRouter({
14
routes: [{
15
path: '/',
16
name: 'home',
17
component: Home
18
},{
19
path: '/register',
20
name: 'register',
21
component: Register
22
},{
23
path: '/login',
24
name: 'login',
25
component: Login
26
}]
27
});
@websanova/vue-auth
@websanova/vue-auth
是客户端负责处理认证的库,它会注入一个 $auth
对象来提供很多有用的函数:比如 register()
来处理用户注册,login()
来处理用户登录,user()
来访问当前登录用户数据,logout()
来处理退出操作等等。
首先安装这个库:
1
npm install @websanova/vue-auth
再次编辑 resources/assets/js/app.js
:
1
import Vue from 'vue';
2
import VueRouter from 'vue-router';
3
import axios from 'axios';
4
import VueAxios from 'vue-axios';
5
import App from './App.vue';
6
import Dashboard from './components/Dashboard.vue';
7
import Home from './components/Home.vue';
8
import Register from './components/Register.vue';
9
import Login from './components/Login.vue';
10
Vue.use(VueRouter);
11
Vue.use(VueAxios, axios);
12
axios.defaults.baseURL = 'http://apidemo.test/api';
13
const router = new VueRouter({
14
routes: [{
15
path: '/',
16
name: 'home',
17
component: Home
18
},{
19
path: '/register',
20
name: 'register',
21
component: Register,
22
meta: {
23
auth: false
24
}
25
},{
26
path: '/login',
27
name: 'login',
28
component: Login,
29
meta: {
30
auth: false
31
}
32
},{
33
path: '/dashboard',
34
name: 'dashboard',
35
component: Dashboard,
36
meta: {
37
auth: true
38
}
39
}]
40
});
41
Vue.router = router
42
Vue.use(require('@websanova/vue-auth'), {
43
auth: require('@websanova/vue-auth/drivers/auth/bearer.js'),
44
http: require('@websanova/vue-auth/drivers/http/axios.1.x.js'),
45
router: require('@websanova/vue-auth/drivers/router/vue-router.2.x.js'),
46
});
47
App.router = Vue.router
48
new Vue(App).$mount('#app');
在新增的代码中,我们首先引入刚刚安装的库并且做了一些配置:
使用 bearer 在请求期间添加认证 token 到请求头,以便服务端读取解析这个 token:
1
auth: require(‘@websanova/vue-auth/drivers/auth/bearer.js’)
配置 vue-auth 使用 axios 来发送 HTTP 请求:
1
http: require(‘@websanova/vue-auth/drivers/http/axios.1.x.js’)
我们还配置 vue-auth 使用 vue-router:
1
router: require(‘@websanova/vue-auth/drivers/router/vue-router.2.x.js’)
最后,注意到:
1
meta: {
2
auth: true
3
}
这个配置,该配置用于指定访问路由是否需要认证。
想要了解更多可以访问 @websanova/vue-auth Github 仓库。
现在运行 npm run watch
,再访问后台 http://apidemo.test/#/dashboard
,就会跳转到登录页面:
jwt-auth
本教程中我们将使用 jwt-auth 来实现 API 认证,所以接下来安装这个扩展包:
1
composer require tymon/jwt-auth
安装完成后在配置文件 config/app.php
中注册服务提供者和别名:
1
...
2
'providers' => [
3
...
4
Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
5
]
6
...
7
'aliases' => [
8
...
9
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
10
]
发布资源和配置:
1
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
在发布的配置中生成key:
1
php artisan jwt:generate
如果上述命令执行报错,可以通过这个链接解决。
编辑 app/Http/Kernel.php
添加 jwt.auth
和 jwt.refresh
到应用路由中间件数组:
1
protected $routeMiddleware = [
2
...
3
'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
4
'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class,
5
];
完成以上操作接下来就行注册路由,创建控制器,进行 API 功能验证了。
注册接口实现
现在 routes/api.php
中注册路由:
1
Route::post('auth/register', 'AuthController@register');
创建认证所需控制器:
1
php artisan make:controller AuthController
我们再创建一个 FormRequest
来处理注册请求验证:
1
php artisan make:request RegisterFormRequest
首先编写 FormRequest
处理类的验证规则代码:
1
class RegisterFormRequest extends FormRequest
2
{
3
public function authorize()
4
{
5
return true;
6
}
7
public function rules()
8
{
9
return [
10
'name' => 'required|string|unique:users',
11
'email' => 'required|email|unique:users',
12
'password' => 'required|string|min:6|max:10',
13
];
14
}
15
}
接下来在控制器 AuthController
中创建一个 register
方法:
1
public function register(RegisterFormRequest $request)
2
{
3
$user = new User;
4
$user->email = $request->email;
5
$user->name = $request->name;
6
$user->password = bcrypt($request->password);
7
$user->save();
8
return response([
9
'status' => 'success',
10
'data' => $user
11
], 200);
12
}
学院君友情提醒:直接拷贝代码的同学注意在代码中引入相应类的命名空间,不要报错了又来数落文档不负责,学院君很无辜,文档更无辜?。
最后在 Register.vue
文件最后添加如下代码:
1
<script>
2
export default {
3
data(){
4
return {
5
name: '',
6
email: '',
7
password: '',
8
error: false,
9
errors: {},
10
success: false
11
};
12
},
13
methods: {
14
register(){
15
var app = this
16
this.$auth.register({
17
params: {
18
name: app.name,
19
email: app.email,
20
password: app.password
21
},
22
success: function () {
23
app.success = true
24
},
25
error: function (resp) {
26
app.error = true;
27
app.errors = resp.response.data.errors;
28
},
29
redirect: null
30
});
31
}
32
}
33
}
34
</script>
再次运行 npm run watch
,然后在浏览器中通过 http://apidemo.test/#/register
访问注册页面进行注册:
如果注册失败,报错页面如下:
注册成功则页面显示如下:
登录接口实现
回到 AuthController
添加 login()
方法:
1
public function login(Request $request)
2
{
3
$credentials = $request->only('email', 'password');
4
if ( ! $token = JWTAuth::attempt($credentials)) {
5
return response([
6
'status' => 'error',
7
'error' => 'invalid.credentials',
8
'msg' => 'Invalid Credentials.'
9
], 400);
10
}
11
return response(['status' => 'success'])
12
->header('Authorization', $token);
13
}
此外我们继续添加 user()
和 refresh()
方法到该控制器:
1
public function user(Request $request)
2
{
3
$user = User::find(Auth::user()->id);
4
return response([
5
'status' => 'success',
6
'data' => $user
7
]);
8
}
9
public function refresh()
10
{
11
return response([
12
'status' => 'success'
13
]);
14
}
其中 user()
方法用于获取当前登录用户数据,而 refresh()
方法用于检查当前登录用户 token 是否仍然有效。
当然,不要忘了给上面新增的控制器方法注册路由:
1
Route::post('auth/login', 'AuthController@login');
2
Route::group(['middleware' => 'jwt.auth'], function(){
3
Route::get('auth/user', 'AuthController@user');
4
});
5
Route::group(['middleware' => 'jwt.refresh'], function(){
6
Route::get('auth/refresh', 'AuthController@refresh');
7
});
最后将以下代码添加到 Login.vue
最后:
1
<script>
2
export default {
3
data(){
4
return {
5
email: null,
6
password: null,
7
error: false
8
}
9
},
10
methods: {
11
login(){
12
var app = this
13
this.$auth.login({
14
params: {
15
email: app.email,
16
password: app.password
17
},
18
success: function () {},
19
error: function () {},
20
rememberMe: true,
21
redirect: '/dashboard',
22
fetchUser: true,
23
});
24
},
25
}
26
}
27
</script>
运行 npm run watch
, 进入登录页面 http://apidemo.test/#/login
输入之前注册用户信息进行登录:
登录成功后页面跳转到后台 http://apidemo.test/#/dashboard
:
退出接口实现
最后,我们添加 logout()
方法到控制器 AuthController
:
1
public function logout()
2
{
3
JWTAuth::invalidate();
4
return response([
5
'status' => 'success',
6
'msg' => 'Logged out Successfully.'
7
], 200);
8
}
该方法会确保用户从后台退出,从而使 token 失效,进而从客户端清除。
在 routes/api.php
注册对应路由:
1
Route::group(['middleware' => 'jwt.auth'], function(){
2
...
3
Route::post('auth/logout', 'AuthController@logout');
4
});
然后编辑 App.vue
文件:
1
<template>
2
<div class="panel panel-default">
3
<div class="panel-heading">
4
<nav>
5
<ul class="list-inline">
6
<li>
7
<router-link :to="{ name: 'home' }">首页</router-link>
8
</li>
9
<li v-if="!$auth.check()" class="pull-right">
10
<router-link :to="{ name: 'login' }">登录</router-link>
11
</li>
12
<li v-if="!$auth.check()" class="pull-right">
13
<router-link :to="{ name: 'register' }">注册</router-link>
14
</li>
15
<li v-if="$auth.check()" class="pull-right">
16
<a href="#" @click.prevent="$auth.logout()">退出</a>
17
</li>
18
</ul>
19
</nav>
20
</div>
21
<div class="panel-body">
22
<router-view></router-view>
23
</div>
24
</div>
25
</template>
$auth.check()
用于检查用户是否登录,$auth.logout()
用于用户退出请求。
运行 npm run watch
,刷新 http://apidemo.test/#/dashboard
,页面显示如下:
点击「退出」按钮,用户退出,页面跳转到首页:
至此,我们已经集合 Laravel + Vue 基于 jwt-auth 实现了 API 基本认证功能。下一篇我们来讲讲如何将 Laravel 5.5 新增的 Eloquent API Resource 功能集成进来。