Laravel 5.3 自带多权限登录正确姿势

前言

  • 因为在网上没有看到一篇完整的讲述5.3多权限登录验证的问题,而且以前我们是需要通过插件包来实现,现在5.3中可以直接用内部机制。所以抛砖引玉的讲一天的研究成果成文分享给大家。希望大家快乐学习,也同样乐于分享。

在正式开始之前,请确认已经熟悉对应的laravel-china翻译的官方文档:http://d.laravel-china.org/docs/5.3/authentication

1,建立模型

namespace App\Models;

use App\Models\Traits\AdminTrait;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin  extends Authenticatable
{
    use Notifiable;
    use AdminTrait;

    protected $table = "admins";
……
}

这里注意是extends Authenticatable,而非默认的model

修改config/auth.php

'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
    ],

        'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
  • 在guard数组里面规定了3套验证机制,分别是会话型的user 和 admin,这两种是需要我们进行登录验证的(一般采用中间件方式),另外一种就是前后端分离时会用到的api,这种一般都是通过指定过期的token进行临时性的会话,这也是为了后面控制器中使用guard做准备。同时注意providers中驱动模型采用eloquent方式,也就是数据模型驱动。

2,接下来建立控制器

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Auth;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
     */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/admin/dashboard';

    /**
     * 如果是在地址栏中输入登录url,就需要先判断是否已经登录。
     *
     */
    public function getLogin()
    {
        if ($this->guard()->check()) {
            return redirect()->route('admin.dashboard.index');
        }
        return view('admin.auth.login');
    }

    /**
     * 采用post方法进行登录时采用的验证机制
     *
     */
    public function postLogin(Request $request)
    {
        $this->validate($request, [
            $this->loginUsername() => 'required', 'password' => 'required',
        ]);
        //  $this->validateLogin($request);
        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if ($this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        /////////////////////////////////////////////////////////////////
        //         注意下面采用的是Auth::guard('admin’)验证方式。          //
        ////////////////////////////////////////////////////////////////

        // if ($this->attemptLogin($request)) {
        if (Auth::guard('admin')  //也可以写成:$this->guard(),这样更利于代码的维护,原因自行分析。
            ->attempt(['email'=>$request->email, 'password'=>$request->password],
            $request->has('remember'))) {
        //if (Auth::attempt(['email'=>$request->email,'password'=>$request->password], $request->has('remember')))            {

               return redirect()->route('admin.dashboard.index');
        }

        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }

    /**
     * 可选择不同的字段作为校验和登录名用。
     * 比如,你登录是采用用户名登录,这里就return 'name‘,如果你是想用email进行登录,就在这里返回email字符串
     * 是不是非常方便?
     */
    public function loginUsername()
    {
        return 'email';
    }

    protected function guard()
    {
        return Auth::guard('admin');
    }

    /**
     * 退出登录,同时跳转到登录页面。
     *
     */
    public function getLogout(Request $request)
    {
        $this->guard()->logout();

        return redirect()->route('admin.login');
    }
}
  • 5.1中是没有采用guard的,所以这里需要大家先看官方文档,再来看本文的原因所在。
  • 被注释的验证代码是5.1中的验证方式,大家可以对比看看有哪些不同。

3,路由

3.1 配置Kernel.php

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        'throttle:60,1',
        'bindings',
    ],

    'admin' => [
        \App\Http\Middleware\AdminAuthenticate::class,
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

/**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
    'admin'       => \App\Http\Middleware\AdminAuthenticate::class,
    'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
  • 这里面的middlewareGroups里的admin中也必须加上各种必须中间件,不然在routes/admin.php中虽然加上了“admin”中间件,还是无法进入对应的方法中进行验证。其他组件也必须加上,不然cookies,sessions,CSRF 的功能就没有了。其中,
  • web 一般是我们用于前端的各种操作进行处理;
  • api 是进行无状态回话接口设计,方便前后端分离,或者其他站点授权访问机制;
  • admin 主要是用于后台管理方面;

上面AdminAuthenticate.php具体内容:

<?php

namespace App\Http\Middleware;

use Auth;
use Closure;

class AdminAuthenticate
{
    public function handle($request, Closure $next)
    {
        if (!Auth::guard('admin')->check()) {
            return redirect()->route('admin.login');
        }

        return $next($request);
    }
}

3.2 配置RouteServiceProvider.php

  • 这里配置的目的是将不同用途的路由拆分到不同的文件中,同时采取不同的验证机制,便于合理组织我们的代码。
public function map()
{
    $this->mapApiRoutes();
    $this->mapWebRoutes();
    $this->mapAdminRoutes();
}

protected function mapWebRoutes()
{
    Route::group([
        'middleware' => 'web',
        'namespace'  => $this->namespace,
    ], function ($router) {
        require base_path('routes/web.php');
    });
}

protected function mapApiRoutes()
{
    Route::group([
        'middleware' => 'api',
        'namespace'  => $this->namespace,
        'prefix'     => 'api',
    ], function ($router) {
        require base_path('routes/api.php');
    });
}

protected function mapAdminRoutes()
{
    Route::group([
        'namespace'  => 'App\Http\Controllers\Admin',
        'prefix'     => 'admin',
        'middleware' => 'web',
    ], function ($router) {
        require base_path('routes/admin.php');
    });
}

这里面特别要强调mapAdminRoutes方法,这里面的middleware千万不要写成admin了,不然会形成无限死循环的。始终会进入AdminAuthenticate.php中的验证方法出不去。

3.3 配置routes/admin.php

Route::get('login', 'LoginController@getLogin')->name('admin.login');
Route::post('login', 'LoginController@postLogin')->name('admin.post.login');
Route::get('logout', 'LoginController@getLogout')->name('admin.logout');

Route::group(['middleware' => 'admin', 'as' => 'admin.'], function () {
    Route::get('/', 'HomeController@index');
    Route::get('/dashboard', 'DashboardController@index')->name('dashboard.index');

}

重要的事情再次强调下:

  • 上面Kernel中的middlewareGroups【admin】中也必须加上\App\Http\Middleware\AdminAuthenticate::class,,不然在routes/admin.php中虽然加上了“admin”中间件,还是无法进入对应的方法中进行验证。

2017.7.30
[TODO]
忘了写config下面的auth配置,晚上补上。
[DONE] 2017.7.30 10时补充完整。

本文章首发在 Laravel China 社区

努力是不会骗人的!