12/08/2018, 17:23

Tạo form đăng nhập (Laravel + vue.js)

I. Chuẩn bị Tạo project laravel lara-vue-auth bằng câu lệnh sau: composer create-project laravel/laravel lara-vue-auth --prefer-dist Sau khi tạo được project lara-vue-auth ta di chuyển vào thư mục gốc và cài đặt npm cd lara-vue-auth npm install Chỉnh sửa file .env kết nốt ...

I. Chuẩn bị

  • Tạo project laravel lara-vue-auth bằng câu lệnh sau:
    composer create-project laravel/laravel lara-vue-auth --prefer-dist
    
  • Sau khi tạo được project lara-vue-auth ta di chuyển vào thư mục gốc và cài đặt npm
    cd lara-vue-auth
    npm install
    
  • Chỉnh sửa file .env kết nốt database và chạy lệnh migrate để tạo bảng users, *password_resets *:
    php artisan migrate
    
  • Cài đặt một vài thư viện vue cần thiết với câu lệnh:
    npm install --save-dev vue-axios vue-router vue-loader vue-template-compiler
    

II. Vue component

  • Tạo file App.vue trong thư mục resource/assets/js/ với nội dung:
    <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 một file khác với tên Home.vue nhưng ở trong thư mục resources/assets/js/components với nội dung:
    <template>
        <h1>Laravel 5 Vue SPA Authentication</h1>
    </template>
    
  • Tiếp theo ta update file resource/assets/js/app.js:
    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)
    });
    
  • Ta thay đổi nội dung resources/views/welcome.blade.php thành 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>
    
  • Trước khi test thử ta cần compile file app.js bằng câu lệnh:
    npm run dev
    
  • Đến đây ta sẽ chạy 1 server để test thử:
    php artisan serve
    
  • Truy cập vào đường link http://localhost:8000 trên trình duyệt để xem kết quả.
  • Tiếp đến ta sẽ tạo component vue với tên Register.vue trong thư mục resources/assets/js/components:
    //Register.vue
    
    <template>
        <div>
            <div class="alert alert-danger" v-if="error && !success">
                <p>There was an error, unable to complete registration.</p>
            </div>
            <div class="alert alert-success" v-if="success">
                <p>Registration completed. You can now <router-link :to="{name:'login'}">sign in.</router-link></p>
            </div>
            <form autocomplete="off" @submit.prevent="register" v-if="!success" method="post">
                <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 thêm một file nữa cũng trong thư mục trên với tên Login.vue:
    <template>
        <div>
            <div class="alert alert-danger" v-if="error">
                <p>There was an error, unable to sign in with those credentials.</p>
            </div>
            <form autocomplete="off" @submit.prevent="login" method="post">
                <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>
    
  • Và tạo thêm file cuối Dashboard.vue:
    <template>
        <h1>Laravel 5 – Our Cool Dashboard</h1>
    </template>
    
  • Cài đặt thư viện @websanova/vue-auth:
    npm install @websanova/vue-auth
    
  • Thư viện này hỗ trợ việc xác thực ở phía client với một số hàm register, login, logout, user
  • Đến đây ta cần update lại 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,
            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');
    
  • Trong đoạn code ở trên ta đã include thư viện vừa cài đặt và cho phép một số cấu hình làm việc với nó:
    auth: require(‘@websanova/vue-auth/drivers/auth/bearer.js’)
    
  • Dòng cấu hình vue-auth bên trên sử dụng bearer dirver để thêm authe token vào header request và đọc, parse token từ server trả về.
  • Ngoài ra vue-auth còn sử dụng axios http driver và vue-router:
    http: require(‘@websanova/vue-auth/drivers/http/axios.1.x.js’)
    router: require(‘@websanova/vue-auth/drivers/router/vue-router.2.x.js’)
    
  • Bạn có thể tìm hiểu thêm về thư viện @websanova/vue-auth tại đây.
  • Chạy npm run watch và thử truy cập vào dashboard từ trình duyệt, nó sẽ tự động chuyển đến trang login.

III. Server laravel

1. Jwt-auth

  • Cài đặt thư viện jwt-auth bằng composer
    composer require tymon/jwt-auth
    
  • Thêm provider và facade vào config/app.php:
     ...
    'providers' => [
        ...
        TymonJWTAuthProvidersJWTAuthServiceProvider::class,
    ]
    ...
    'aliases' => [
        ...
        'JWTAuth' => TymonJWTAuthFacadesJWTAuth::class,
    ]
    
  • Và ta cần publish thư viện jwt-auth bằng câu lệnh:
    php artisan vendor:publish --provider="TymonJWTAuthProvidersJWTAuthServiceProvider"
    
  • Tạo key publish với lệnh:
    php artisan jwt:generate
    
  • Nếu gặp lỗi JWTGenerateCommand::handle() does not exist thì bạn chỉ cần thêm phương thức handle() vào file vendor/tymon/jwt-auth/src/Commands/JWTGenerateCommand.php
    public function handle() {
        $this->fire();
    }
    
  • Thêm route jwt.authjwt.refresh vào app/Http/Kernel.php
    protected $routeMiddleware = [
        ...
        'jwt.auth' => TymonJWTAuthMiddlewareGetUserFromToken::class,
        'jwt.refresh' => TymonJWTAuthMiddlewareRefreshToken::class,
    ];
    

2. Registration

  • Tạo AuthController:
    php artisan make:controller AuthController
    
  • Và thêm route vào file routes/api.php:
    Route::post(‘auth/register’, ‘AuthController@register’);
    
  • Tạo requestForm để thực hiện validate khi đăng ký user
    php artisan make:request RegisterFormRequest
    
  • Và sửa nội dung file RegisterFormRequest.php thành như sau:
    ...
    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',
            ];
        }
    }
    
  • Tạo phương thức register trong AuthController.php:
    ...
    use AppUser;
    use AppHttpRequestsRegisterFormRequest;
    ...
    
    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);
     }
    
  • Đến đây ta cần thêm đoạn code vue gọi đến phương thức trên vào cuối file Register.vue:
    <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>
    
  • Giờ ta có thể chạy npm run watch và đăng ký thử một user

3. Login

  • Trở lại file AuthController ta thêm phương thức login:
    ...
    use JWTAuth;
    use Auth;
    ...
    
    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 2 phương thức user()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'
            ]);
    }
    
  • File route/api.php sẽ cần update thêm:
    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');
    });
    
  • Cuối cùng là thêm phần kết nối vue với controller vào cuối file Login.vue:
    <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: 
                                              
0