Đây là tutorial hướng dẫn bạn các bước thực hiện cung cấp 1 authentication cho Vue Single Page Application (SPA) cần xác thực để access API trong Laravel.
Resources cần chuẩn bị:
- NodeJS 8.9.1
- Laravel 5.5
- jwt-auth 0.5.12
- NPM 5.6.0
- VueJS 2.5.7
- Vue-router
- Vue-axios
- @websanova/vue-auth
Cài đặt
Tạo một project laravel bằng cách thực hiện command trong terminal
composer create-project laravel/laravel lara-vue-auth –prefer-dist
Di chuyển vào thư mục project
cd lara-vue-auth
Tiếp theo bạn cần cài đặt javascript dependencies bằng cách sử dụng command
npm install
Bước tiếp theo cung cấp các config để kết nối database trong file .env và tạo db để sử dụng nếu bạn chưa có. Để tạo 1 db trong Laravel thì dùng lệnh sau:
php artisan migrate
Lệnh này sẽ tạo cho bạn 1 db với bản users
và password_resets
Tiếp tục là cài đặt một số thư viện Vue mà chúng ta cần.
npm install --save-dev vue-axios vue-router vue-loader vue-template-compiler
Tạo một file với tên là App.vue
trong resources/assets/js
và bỏ vào file nội dung sau:
<template> <div class="panel panel-default"> <div class="panel-heading"> <nav> <ul class="list-inline"> <li> <router-link :to="{ name: 'home' }">Home</router-link> </li> <li class="pull-right"> <router-link :to="{ name: 'login' }">Login</router-link> </li> <li class="pull-right"> <router-link :to="{ name: 'register' }">Register</router-link> </li> </ul> </nav> </div> <div class="panel-body"> <router-view></router-view> </div> </div> </template>
Tạo 1 file khác với tên Home.vue
trong resources/assets/js/components
và thêm:
<template> <h1>Laravel - Vue SPA Authentication</h1> </template>
Sau đó replace nội dung resouces/assets/js/app.js
với nội dung sau:
import Vue from 'vue'; import VueRouter from 'vue-router'; import App from './App.vue'; import Home from './components/Home.vue'; Vue.use(VueRouter); const router = new VueRouter({ routes: [ { path: '/', name: 'home', component: Home }, ] }); new Vue({ el: '#app', router: router, render: app => app(App) });
Kế tiếp thay đổi content của template resources/views/welcome.blade.php
như sau:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>Laravel</title> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container"> <div id="app"></div> </div> <script src="/js/app.js"></script> </body> </html>
Bây giờ chạy thử command
npm run watch
và
php artisan serve
Mở browser và gõ http://localhost:8000. Nếu mọi chuyện ổn thì bạn sẽ thấy homepage nghen. Xong phần cơ bản!
Tạo Vue Components
Tạo file với tên Register.vue
trong thư mục resources/assets/js/components
với nội dung sau:
<template> <div> <div class="alert alert-danger" v-if="error && !success"> <p>Có lỗi đâu đó, đăng ký không thành công.</p> </div> <div class="alert alert-success" v-if="success"> <p>Đăng ký thành công. Bạn có thể <router-link :to="{name:'login'}">sign in.</router-link></p> </div> <form autocomplete="off" @submit.prevent="register" v-if="!success"> <div class="form-group" v-bind:class="{ 'has-error': error && errors.name }"> <label for="name">Name</label> <input type="text" id="name" class="form-control" v-model="name" required> <span class="help-block" v-if="error && errors.name">{{ errors.name }}</span> </div> <div class="form-group" v-bind:class="{ 'has-error': error && errors.email }"> <label for="email">E-mail</label> <input type="email" id="email" class="form-control" placeholder="user@example.com" v-model="email" required> <span class="help-block" v-if="error && errors.email">{{ errors.email }}</span> </div> <div class="form-group" v-bind:class="{ 'has-error': error && errors.password }"> <label for="password">Password</label> <input type="password" id="password" class="form-control" v-model="password" required> <span class="help-block" v-if="error && errors.password">{{ errors.password }}</span> </div> <button type="submit" class="btn btn-default">Submit</button> </form> </div> </template>
Tạo file khác với tên Login.vue
trong cùng một thư mục với nội dung sau:
<template> <div> <div class="alert alert-danger" v-if="error"> <p>Có lỗi đâu đó, không thể login!</p> </div> <form autocomplete="off" @submit.prevent="login"> <div class="form-group"> <label for="email">E-mail</label> <input type="email" id="email" class="form-control" placeholder="user@example.com" v-model="email" required> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" id="password" class="form-control" v-model="password" required> </div> <button type="submit" class="btn btn-default">Sign in</button> </form> </div> </template>
Lại tiếp tục tạo thêm file Dashboard.vue
cùng thư mục và thêm:
<template> <h1>Laravel – Dashboard</h1> </template>
Sau đó replace content của file resources/assets/js/app.js
:
import Vue from 'vue'; import VueRouter from 'vue-router'; import axios from 'axios'; import VueAxios from 'vue-axios'; import App from './App.vue'; import Dashboard from './components/Dashboard.vue'; import Home from './components/Home.vue'; import Register from './components/Register.vue'; import Login from './components/Login.vue'; Vue.use(VueRouter); Vue.use(VueAxios, axios); axios.defaults.baseURL = 'http://localhost:8000/api'; const router = new VueRouter({ routes: [{ path: '/', name: 'home', component: Home },{ path: '/register', name: 'register', component: Register },{ path: '/login', name: 'login', component: Login }] });
@websanova/vue-auth
Đây là thư viện chịu trách nhiệm xử lý các chứng thực tại client side. Nhiệm vụ của nó là thêm 1 object $auth
với cung cấp một số chức năng trợ giúp như register()
xử lý đăng ký người dùng, login()
xử lý đăng nhập, user()
cho phép truy cập vào dữ liệu người dùng hiện tại, rồi logout()
và một vài chức năng khác.
Cài đặt @websanova/vue-auth
npm install @websanova/vue-auth
Sau đó modify là app.js
như thế này:
import Vue from 'vue'; import VueRouter from 'vue-router'; import axios from 'axios'; import VueAxios from 'vue-axios'; import App from './App.vue'; import Dashboard from './components/Dashboard.vue'; import Home from './components/Home.vue'; import Register from './components/Register.vue'; import Login from './components/Login.vue'; Vue.use(VueRouter); Vue.use(VueAxios, axios); axios.defaults.baseURL = 'http://localhost:8000/api'; const router = new VueRouter({ routes: [{ path: '/', name: 'home', component: Home },{ path: '/register', name: 'register', component: Register, meta: { auth: false } },{ path: '/login', name: 'login', component: Login, meta: { auth: false } },{ path: '/dashboard', name: 'dashboard', component: Dashboard, meta: { auth: true } }] }); Vue.router = router Vue.use(require('@websanova/vue-auth'), { auth: require('@websanova/vue-auth/drivers/auth/bearer.js'), http: require('@websanova/vue-auth/drivers/http/axios.1.x.js'), router: require('@websanova/vue-auth/drivers/router/vue-router.2.x.js'), }); App.router = Vue.router new Vue(App).$mount('#app');
Đoạn code trên chúng ta đã included thêm thư viện vừa cài và thêm 1 số config cần để hoạt động với nó.
Cấu hình vue-auth
để sử dụng trình điều khiển bearer, nó thêm những authen token cho những request header trong quá trình requests, đọc và parse token từ phản hồi của server:
auth: require('@websanova/vue-auth/drivers/auth/bearer.js')
Tùy chọn config vue-auth
để sử dụng axios http driver, vì chúng ta đang sử dụng axios cho các http request:
http: require('@websanova/vue-auth/drivers/http/axios.1.x.js')
Cấu hình vue-auth
để sử dụng driver cho vue-router
router: require(‘@websanova/vue-auth/drivers/router/vue-router.2.x.js’)
Chúng ta cũng thêm tùy chọn meta cho route:
... meta: { auth: true } ...
Thuộc tính auth
xác định xem cần có sự cho phép access route hay không. True
nghĩa là bạn xác định authorization này được quyền access vào dashboard.
Bạn nên xem thêm @websanova/vue-auth để biết cách sử dụng thư viện này nhé. Giờ chạy lệnh:
npm run watch
và truy cập vào dashboard trên browser. Chạy đúng thì nó sẽ redirect bạn đến trang login nha.
Jwt-auth
Tiếp tục chúng ta cần cài đặt thư viện jwt-auth
trong laravel, thư viện này xử lý các authentication qua các api của chúng ta.
Chạy dòng lệnh trong terminal sau:
composer require tymon/jwt-auth
Sau đó add service JWTAuthServceProvider
cho mảng providers và JWTAuth
facade cho mảng aliases trong config/app.php
... 'providers' => [ ... TymonJWTAuthProvidersJWTAuthServiceProvider::class, ] ... 'aliases' => [ ... 'JWTAuth' => TymonJWTAuthFacadesJWTAuth::class, ]
Publish thử xem:
php artisan vendor:publish --provider="TymonJWTAuthProvidersJWTAuthServiceProvider"
Và generate key trong cấu hình được publish bên trên:
php artisan jwt:generate
Note: Nếu bị lỗi thì bạn check link này xem nhé.
Edit app/Http/Kernel.php
để thêm jwt.auth
và jwt.refresh
protected $routeMiddleware = [ ... 'jwt.auth' => TymonJWTAuthMiddlewareGetUserFromToken::class, 'jwt.refresh' => TymonJWTAuthMiddlewareRefreshToken::class, ];
Đăng ký
Trước khi chúng ta đi sâu hơn thì cần phải tạo 1 controller và thêm required route ở routes/api.php
Tạo 1 controller cho authen:
php artisan make:controller AuthController
Thêm route
Route::post(‘auth/register’, ‘AuthController@register’);
Tạo thêm 1 FormRequest
để handle validation cho tất cả registration request nhé:
php artisan make:request RegisterFormRequest
Mình chỉnh lại RegisterFormRequest
một chút:
... class RegisterFormRequest extends FormRequest { public function authorize() { return true; } public function rules() { return [ 'name' => 'required|string|unique:users', 'email' => 'required|email|unique:users', 'password' => 'required|string|min:6|max:10', ]; } }
Bây giờ tiếp tục tạo phương thức sẽ handle các user registration trong AuthController
public function register(RegisterFormRequest $request) { $user = new User; $user->email = $request->email; $user->name = $request->name; $user->password = bcrypt($request->password); $user->save(); return response([ 'status' => 'success', 'data' => $user ], 200); }
RegisterFormRequest
class đảm bảo mỗi yêu cầu tuân thủ các quy tắc mà chúng ta đã thiết lập trong phương thức rule()
. Sau đó phương thức register()
thu thập các input của user từ biến $request
, mã hóa mật khẩu và tiếp tục đưa dữ liệu người dùng vào database.
Quay lại Vue và connect những gì ta đã làm. Đi tới Register.vue
file và thêm đoạn code sau ở dưới cùng nhé:
Now let us go over to vue and connect the dots. Go to your Register.vue file and append the code below to the end of the file. <script> export default { data(){ return { name: '', email: '', password: '', error: false, errors: {}, success: false }; }, methods: { register(){ var app = this this.$auth.register({ params: { name: app.name, email: app.email, password: app.password }, success: function () { app.success = true }, error: function (resp) { app.error = true; app.errors = resp.response.data.errors; }, redirect: null }); } } } </script>
Rồi đăng ký thử xem sao, nếu không có lỗi xảy ra thì bạn sẽ đăng ký được (tất nhiên :v):
npm run watch
Đăng nhập
Quay trở lại AuthController
, chúng ta add thêm phương thức login()
public function login(Request $request) { $credentials = $request->only('email', 'password'); if ( ! $token = JWTAuth::attempt($credentials)) { return response([ 'status' => 'error', 'error' => 'invalid.credentials', 'msg' => 'Invalid Credentials.' ], 400); } return response([ 'status' => 'success' ]) ->header('Authorization', $token); }
Thêm phương thức user()
và refresh()
public function user(Request $request) { $user = User::find(Auth::user()->id); return response([ 'status' => 'success', 'data' => $user ]); } public function refresh() { return response([ 'status' => 'success' ]); }
Phương thức user()
được dùng để fetch data của user trong khi phương thức refresh()
là để refresh token hiện tại.
Nối thêm đống code dưới đây trong routes/api.php
:
Route::post('auth/login', 'AuthController@login'); Route::group(['middleware' => 'jwt.auth'], function(){ Route::get('auth/user', 'AuthController@user'); }); Route::group(['middleware' => 'jwt.refresh'], function(){ Route::get('auth/refresh', 'AuthController@refresh'); });
Rồi cho connect với Vue thử xem. Trước hết đi tới Login.vue rồi thêm mớ code bên dưới:
<script> export default { data(){ return { email: null, password: null, error: false } }, methods: { login(){ var app = this this.$auth.login({ params: { email: app.email, password: app.password }, success: function () {}, error: function () {}, rememberMe: true, redirect: '/dashboard', fetchUser: true, }); }, } } </script>
Rồi login thử xem nghen
npm run watch
Đăng xuất
Thêm phương thức logout()
trong AuthController
public function logout() { JWTAuth::invalidate(); return response([ 'status' => 'success', 'msg' => 'Logged out Successfully.' ], 200); }
Phương thức này đảm bảo rằng người dùng đăng xuất khỏi ứng dụng và làm mất hiệu lực của authentication token và xóa nó khỏi client side.
Giờ thì add them route trong routes/api.php
Route::group(['middleware' => 'jwt.auth'], function(){ ... Route::post('auth/logout', 'AuthController@logout'); });
Xong thì modify App.vue
<template> <div class="panel panel-default"> <div class="panel-heading"> <nav> <ul class="list-inline"> <li> <router-link :to="{ name: 'home' }">Home</router-link> </li> <li v-if="!$auth.check()" class="pull-right"> <router-link :to="{ name: 'login' }">Login</router-link> </li> <li v-if="!$auth.check()" class="pull-right"> <router-link :to="{ name: 'register' }">Register</router-link> </li> <li v-if="$auth.check()" class="pull-right"> <a href="#" @click.prevent="$auth.logout()">Logout</a> </li> </ul> </nav> </div> <div class="panel-body"> <router-view></router-view> </div> </div> </template>
Xong rồi thử logout nha anh em:
npm run watch
Tham khảo thêm các vị trí cho lập trình viên Laravel ứng tuyển tại đây