3. Sentinel 2.0 用戶認證應用

安裝好 Sentinel 2.0後,我們要來建立用戶的認證功能,包括註冊、登入、忘記密碼以及密碼重置,這部分改動的較大,請記得自己備份....

我的權限群組可分為三種:系統管理員admin & 公司主管boss &公司員工user
員工的部分只要帳號有啟用就預設是user了,所以這部分暫時不理他,我們需要額外新增兩個role群組權限,來個別定義這兩個權限的內容。

首先,我們先到Kernel去註冊中介層的路由名稱,方便我們直接透過路由來限制存取。
開啟 app/Http/Kernel.php,新增admin & boss 的role權限
   /**  
    * The application's route middleware.  
    *  
    * @var array  
    */  
   protected $routeMiddleware = [  
     'auth'   => \App\Http\Middleware\Authenticate::class,  
     'guest'  => \App\Http\Middleware\RedirectIfAuthenticated::class,  
     'admin'  => \App\Http\Middleware\SentinelAdminAccess::class,  
     'boss'   => \App\Http\Middleware\SentinelBossAccess::class,  
   ];  

新增 app/Http/Middleware/SentinelAdminAccess.php
 <?php  
 namespace App\Http\Middleware;  
 use Closure;  
 use Sentinel;  
 class SentinelAdminAccess  
 {  
   /**  
    * Handle an incoming request.  
    *  
    * @param \Illuminate\Http\Request $request  
    * @param \Closure $next  
    * @return mixed  
    */  
   public function handle($request, Closure $next)  
   {  
     // First make sure there is an active session  
     if (!Sentinel::check()) {  
       if ($request->ajax()) {  
         return response('Unauthorized.', 401);  
       } else {  
         return redirect()->guest(route('auth.login'));  
       }  
     }  
     // Now check to see if the current user has the 'Admin' permission  
     if (!Sentinel::getUser()->inRole('Admin')) {  
       if ($request->ajax()) {  
         return response('Unauthorized.', 401);  
       } else {  
         return back()->withInput()->withErrors(trans('auth.user.noaccess'));  
       }  
     }  
     // All clear - we are good to move forward  
     return $next($request);  
   }  
 }  

開啟 app/Http/Middleware/SentinelBossAccess.php
 <?php  
 namespace App\Http\Middleware;  
 use Closure;  
 use Sentinel;  
 use Session;  
 class SentinelBossAccess  
 {  
   /**  
    * Handle an incoming request.  
    *  
    * @param \Illuminate\Http\Request $request  
    * @param \Closure $next  
    * @return mixed  
    */  
   public function handle($request, Closure $next)  
   {  
     // First make sure there is an active session  
     if (!Sentinel::check()) {  
       if ($request->ajax()) {  
         return response('Unauthorized.', 401);  
       } else {  
         return redirect()->guest(route('auth.login'));  
       }  
     }  
     // Now check to see if the current user has the 'Boss' permission  
     if (!Sentinel::getUser()->inRole('Boss')) {  
       if ($request->ajax()) {  
         return response('Unauthorized.', 401);  
       } else {  
         Session::flash('error', trans('auth.user.noaccess'));  
         return redirect()->route('auth.login');  
       }  
     }  
     // All clear - we are good to move forward  
     return $next($request);  
   }  
 }  

接下來,我們要來設定所有用戶的登入認證與授權
開啟 app/Http/Middleware/Authenticate.php
 <?php  
 namespace App\Http\Middleware;  
 use Closure;  
 use Sentinel;  
 class Authenticate  
 {  
   /**  
    * Handle an incoming request.  
    *  
    * @param \Illuminate\Http\Request $request  
    * @param \Closure $next  
    * @return mixed  
    */  
   public function handle($request, Closure $next)  
   {  
     if (Sentinel::guest()){  
       if ($request->ajax()) {  
         return response('Unauthorized.', 401);  
       } else {  
         return redirect()->guest('auth/login');  
       }  
     }  
     return $next($request);  
   }  
 }  

開啟 app/Http/Controllers/Auth/PasswordController.php
 <?php  
 namespace App\Http\Controllers\Auth;  
 use App\Http\Controllers\Controller;  
 use Input;  
 use Validator;  
 use Sentinel;  
 use Reminder;  
 use Mail;  
 class PasswordController extends Controller  
 {  
   /*  
    * views模板頁面-忘記密碼  
    */  
   public function getReminder() {return view('auth.password'); }  
   /*  
    * views模板頁面-重置密碼  
    */  
   public function getReset(){return view('auth.reset'); }  
   /**  
    * 忘記密碼  
    *  
    * @param array $data  
    * @return User  
    */  
   public function postReminder()  
   {  
     $input = Input::all();  
     // 驗證欄位  
     $rules = [  
       'email'  => 'required|email|max:255',  
     ];  
     $validation = Validator::make($input, $rules);  
     if ($validation->fails()) {  
       return back()->withErrors($validation)->withInput();  
     }  
     $user = Sentinel::findByCredentials($input);  
     if(!$user){  
       return back()->withErrors(trans('auth.password.email_fail'))->withInput();  
     }  
     $reminder = Reminder::exists($user) ?: Reminder::create($user);  
     $code = $reminder->code;  
     $sent = Mail::send('emails.password', compact('user', 'code'), function($m) use ($user) {  
       $m->to($user->email)->subject(trans('auth.password.email_title'));   
     });   
     // 寄送帳號驗證信失敗  
     if ($sent === 0) {  
       return redirect()->route('auth.register')->withErrors(trans('auth.password.send_fail'));  
     }  
     return back()->withSuccess(trans('auth.password.send_success'));  
   }  
   /**  
    * 變更密碼  
    *  
    * @param array $data  
    * @return User  
    */  
   public function postReset()  
   {  
     $input = Input::all();  
     $rules = [  
       'email'   => 'required|email|max:255',  
       'password'  => 'required|confirmed|min:6',  
     ];  
     $validation = Validator::make($input, $rules);  
     if ($validation->fails()) {  
       return back()->withErrors($validation)->withInput();  
     }  
     $user = Sentinel::findByCredentials($input);  
     if(!$user){  
       return back()->withErrors(trans('auth.password.email_fail'))->withInput();  
     }  
     $user = $this->update($user, $input);  
     return redirect()->route('auth.login')  
         ->withSuccess(trans('password_success'));  
   }  
   /*  
    * Once you have authenticated, a session has been created.   
    * At this point you'll most likely want to do something with the users meta data.  
    */  
   public function update($user, $input)  
   {  
     return Sentinel::update($user, $input);  
   }  
 }  

然後我們就可以設定相關的路由定義了
開啟 app/Http/routes.php
 // 認證路由  
 Route::group(['prefix' => 'auth', 'namespace' => 'Auth'], function () {  
   // 登入路由  
   Route::get('login', ['as' => 'auth.login', 'uses' => 'AuthController@getLogin']);  
   Route::post('login', ['as' => 'auth.login', 'uses' => 'AuthController@postLogin']);  
   // 註冊路由  
   Route::get('register', ['as' => 'auth.register', 'uses' => 'AuthController@getRegister']);  
   Route::post('register', ['as' => 'auth.register', 'uses' => 'AuthController@postRegister']);  
   // 登出路由  
   Route::get('logout', ['as' => 'auth.logout', 'uses' => 'AuthController@getLogout']);  
   // 驗證信箱路由   
   Route::get('verify/{id}/{code}', ['as' => 'auth.verify', 'uses' => 'AuthController@getConfirm']);   
 });  
 // 重設密碼路由  
 Route::group(['prefix' => 'password', 'namespace' => 'Auth'], function () {  
   // 申請密碼重置的路由  
   Route::get('email', ['as' => 'password.email', 'uses' => 'PasswordController@getReminder']);  
   Route::post('email', ['as' => 'password.email', 'uses' => 'PasswordController@postReminder']);  
   // 密碼重置的路由  
   Route::get('reset/{id}/{code}', ['as' => 'password.reset', 'uses' => 'PasswordController@getReset']);  
   Route::post('reset', ['as' => 'password.reset', 'uses' => 'PasswordController@postReset']);  
 });  
 // 前台首頁  
 Route::get('/', function () {return view('welcome'); });  
 // 後台首頁路由  
 Route::group(['prefix' => 'admin', 'namespace' => 'Admin', 'middleware' => 'auth'], function () {  
     Route::group(['middleware' => 'admin'], function (){  
         Route::get('system_manage', ['as' => 'admin.system_manage', 'uses' => 'HomeController@index']);   
     });  
     Route::group(['middleware' => 'boss'], function (){  
         Route::get('user_manage', ['as' => 'admin.permission_manage', 'uses' => 'HomeController@index']);   
     });  
     // 一般員工只能看到這頁
     Route::get('/', ['as' => 'admin.root', 'uses' => 'HomeController@index']);   
 });  

產生資料填充檔
 $ php artisan make:seeder RoleTableSeeder  
 $ php artisan make:seeder UserTableSeeder  

這裏我們新增了兩筆資料,admin的權限為最大,公司主管只能使用 user.user_manage
開啟 database/seeds/RoleTableSeeder.php
 <?php  
 use Illuminate\Database\Seeder;  
 class RoleTableSeeder extends Seeder  
 {  
   /**  
    * Run the database seeds.  
    *  
    * @return void  
    */  
   public function run()  
   {  
     DB::table('roles')->delete();  
     Sentinel::getRoleRepository()->createModel()->create([  
         'name'    => '系統管理員',  
             'slug'    => 'Admin',  
             'permissions' => $this->Admin(),  
     ]);  
     Sentinel::getRoleRepository()->createModel()->create([  
         'name'    => '公司管理員',  
             'slug'    => 'Boss',  
             'permissions' => $this->Boss(),  
     ]);  
   }  
   private function Admin(){  
       return $data = array(  
             'user.user_manage'       => true,  
             'sys.system_manage'      => true,  
             'sys.rorle_manage'       => true,  
             'sys.permission_manage'    => true,  
             'sys.news_manage'       => true,  
       );  
   }  
   private function Boss(){  
         $data             = $this->Admin();  
         $data['sys.system_manage']   = false;  
         $data['sys.rorle_manage']   = false;  
         $data['sys.permission_manage'] = false;  
         $data['sys.news_manage']    = false;  
       return $data;  
   }  
 }  

開啟 database/seeds/UserTableSeeder.php
 <?php  
 use Illuminate\Database\Seeder;  
 class UserTableSeeder extends Seeder  
 {  
   /**  
    * Run the database seeds.  
    *  
    * @return void  
    */  
   public function run()  
   {  
     DB::table('users')->delete();  
     DB::table('role_users')->delete();  
     DB::table('activations')->delete();  
     $user = Sentinel::create([  
             'email'   => 'admin@abc.com',  
             'first_name' => '系統管理員',  
             'password'  => '111111',  
     ]);  
     // 啟用帳號  
     $activation = Activation::create($user);  
     Activation::complete($user, $activation->code);  
     // Assign a user to a role.  
     $role = Sentinel::findRoleBySlug('Admin');  
         $role->users()->attach($user);  
     $user = Sentinel::create([  
             'email'   => 'boss@abc.com',  
             'first_name' => '公司管理員',  
             'password'  => '111111',  
     ]);  
     // 啟用帳號  
     $activation = Activation::create($user);  
     Activation::complete($user, $activation->code);  
     // Assign a user to a role.  
     $role = Sentinel::findRoleBySlug('SuUser');  
         $role->users()->attach($user);  
     Sentinel::create([  
             'email'   => 'user@abc.com',  
             'first_name' => '公司員工',  
             'password'  => '111111',  
     ]);  
     // 啟用帳號  
     $activation = Activation::create($user);  
     Activation::complete($user, $activation->code);  
   }  
 }  

開啟 database/seeds/DatabaseSeeder.php
 <?php  
 use Illuminate\Database\Seeder;  
 use Illuminate\Database\Eloquent\Model;  
 class DatabaseSeeder extends Seeder  
 {  
   /**  
    * Run the database seeds.  
    *  
    * @return void  
    */  
   public function run()  
   {  
     Model::unguard();  
     $this->call(RoleTableSeeder::class);  
     $this->call(UserTableSeeder::class);  
     Model::reguard();  
   }  
 }  

執行資料填充:
 $ php artisan db:seed  

最後我們需要新增相關的視圖:
新增 resources/views/auth/login.blade.php
   <form class="login-form" action="/auth/login" method="post">  
     {!! csrf_field() !!}  
     <h3 class="form-title">{{trans('auth.user.login')}}</h3>  
     @include('errors.validation')  
     <div class="form-group">  
       <!--ie8, ie9 does not support html5 placeholder, so we just show field title for that-->  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.user.email')}}</label>  
       <input class="form-control form-control-solid placeholder-no-fix" type="email" autocomplete="off" placeholder="{{trans('auth.user.email')}}" name="email" value="{{ old('email') }}" />  
     </div>  
     <div class="form-group">  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.user.password')}}</label>  
       <input class="form-control form-control-solid placeholder-no-fix" type="password" autocomplete="off" placeholder="{{trans('auth.user.password')}}" name="password" id="password" />  
     </div>  
     <div class="form-actions">  
       <button type="submit" class="btn btn-success uppercase">{{trans('auth.user.btn_login')}}</button>  
       <label class="rememberme check">  
       <input type="checkbox" name="remember"> {{trans('auth.user.remember')}} </label>  
       <a href="{{ url('/password/email') }}" class="forget-password">{{trans('auth.user.forgot')}}</a>  
     </div>  
     {{-- @include('partials.social.authSocial') --}}  
     <div class="create-account">  
       <p>  
         <a href="{{ url('/auth/register') }}" class="uppercase">{{trans('auth.user.register')}}</a>  
       </p>  
     </div>  
   </form>  

新增 resources/views/auth/register.blade.php
   <form action="/auth/register" method="post">  
     {!! csrf_field() !!}  
     <h3>{{trans('auth.user.register')}}</h3>  
     
     @include('errors.validation')  
     <div class="form-group">  
       <!--ie8, ie9 does not support html5 placeholder, so we just show field title for that-->  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.user.email')}}</label>  
       <input class="form-control placeholder-no-fix" type="email" placeholder="{{trans('auth.user.email')}}" name="email" value="{{ old('email') }}"/>  
     </div>  
     <div class="form-group">  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.user.first_name')}}</label>  
       <input class="form-control placeholder-no-fix" type="text" placeholder="{{trans('auth.user.first_name')}}" name="first_name" value="{{ old('first_name') }}" />  
     </div>  
     <div class="form-group">  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.user.last_name')}}</label>  
       <input class="form-control placeholder-no-fix" type="text" autocomplete="off" placeholder="{{trans('auth.user.last_name')}}" name="last_name" value="{{ old('last_name') }}"/>  
     </div>  
     
     <div class="form-group">  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.user.password')}}</label>  
       <input class="form-control placeholder-no-fix" type="password" autocomplete="off" id="register_password" placeholder="{{trans('auth.user.password')}}" name="password"/>  
     </div>  
     <div class="form-group">  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.user.password_confirm')}}</label>  
       <input class="form-control placeholder-no-fix" type="password" autocomplete="off" placeholder="{{trans('auth.user.password_confirm')}}" name="password_confirmation"/>  
     </div>  
     
     <div class="form-actions">  
       <a href="{{ url('/auth/login') }}">  
         <button type="button" id="register-back-btn" class="btn btn-default">{{trans('auth.user.btn_back')}}</button>  
       </a>  
       <button type="submit" id="register-submit-btn" class="btn btn-success uppercase pull-right">{{trans('auth.user.btn_register')}}</button>  
     </div>  
   </form>  

新增 resources/views/auth/password.blade.php
   <form action="/password/email" method="post">  
     {!! csrf_field() !!}  
     <h3>{{trans('auth.password.title')}}</h3>  
     @include('errors.validation')  
     <p> {{trans('auth.password.desc')}}</p>  
     <div class="form-group">  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.user.email')}}</label>  
       <input class="form-control placeholder-no-fix" type="email" autocomplete="off" placeholder="{{trans('auth.user.email')}}" name="email" value="{{ old('email') }}"/>  
     </div>  
     <div class="form-actions">  
       <a href="{{ url('/auth/login') }}">  
         <button type="button" id="back-btn" class="btn btn-default">{{trans('auth.user.btn_back')}}</button>  
       </a>  
       <button type="submit" class="btn btn-success uppercase pull-right">{{trans('auth.password.submit')}}</button>  
     </div>  
   </form>  

新增 resources/views/auth/reset.blade.php
   <form action="/password/reset" method="post">  
     {!! csrf_field() !!}  
     <h3>{{trans('auth.reset.title')}}</h3>  
     @include('errors.validation')  
     <div class="form-group">  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.user.email')}}</label>  
       <input class="form-control placeholder-no-fix" type="email" autocomplete="off" placeholder="{{trans('auth.user.email')}}" name="email" value="{{ old('email') }}"/>  
     </div>  
     <div class="form-group">  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.reset.password')}}</label>  
       <input class="form-control placeholder-no-fix" type="password" autocomplete="off" placeholder="{{trans('auth.reset.password')}}" name="password" />  
     </div>  
     <div class="form-group">  
       <label class="control-label visible-ie8 visible-ie9">{{trans('auth.reset.password_confirmation')}}</label>  
       <input class="form-control placeholder-no-fix" type="password" autocomplete="off" placeholder="{{trans('auth.reset.password_confirmation')}}" name="password_confirmation" />  
     </div>  
     <div class="form-actions">  
       <button type="submit" class="btn btn-success uppercase pull-right">{{trans('auth.reset.submit')}}</button>  
     </div>  
   </form>  

新增 resources/views/emails/password.blade.php
 <!-- resources/views/emails/password.blade.php -->  
 <h2>{{trans('auth.password.email_title')}}</h2>  
 <div>{{trans('auth.password.email_content')}}</div>  
 <div>{{ url('password/reset/'.$user->getUserId().'/'.$code) }}</div>  

新增 resources/views/emails/verify.blade.php
 <!-- resources/views/emails/verify.blade.php -->   
 <h2>{{trans('auth.verify.email_title')}}</h2>   
 <div>{{trans('auth.verify.email_content')}}</div>   
 <div>{{ url('auth/verify/'.$user->getUserId().'/'.$code) }}</div>  

新增 resources/views/errors/validation.blade.php
 <!-- resources/views/errors/validation.blade.php -->  
 @if ($errors->any())  
   <div class="alert alert-danger">  
       <button class="close" data-close="alert"></button>  
       @foreach ($errors->all() as $error)  
       <div>{{ $error }}</div>  
     @endforeach  
     </div>  
 @endif  

再來就用這三個帳號來測試登入以及可用的權限吧,記得要有 HomeController.php 喔!

P.S.
如何加入用戶的登入IP
users table:
$table->string('ip_address')->nullable();

open: /vendor/cartalyst/sentinel/src/Users/IlluminateUserRepository.php

find:
 namespace Cartalyst\Sentinel\Users;

add after:
 use Illuminate\Support\Facades\Request;  

find :
 $user->last_login = Carbon::now();

add after :
 $user->ip_address = Request::ip();

留言

  1. Hi 您好
    再使用 inRole 時有沒有需要注意什麼?
    我在使用時噴出了

    BadMethodCallException in Builder.php line 2071:
    Call to undefined method Illuminate\Database\Query\Builder::getRoleId()

    sentinel ver 2.0.8

    回覆刪除

張貼留言

這個網誌中的熱門文章

2. 新增自定義的全域函式(helpers function)

0. env 基礎設置