Laravel 5.3 多用户表登录

Laravl5.3多用户表登录

关于Laravel5.2多用户表登录文章传送门:http://iwanli.me/article/42

简介

在底层代码中,Laravel 的认证组件由 guardsproviders组成,Guard 定义了用户在每个请求中如何实现认证,例如,Laravel 通过 session guard来维护 Session 存储的状态、Cookie 以及 token guard,token guard 是认证用户发送请求时带的API token。

Provider 定义了如何从持久化存储中获取用户信息,Laravel 底层支持通过 Eloquent 和数据库查询构建器两种方式来获取用户,如果需要的话,你还可以定义额外的 Provider

相对于Laravel5.2而言,Laravel5.3在底层代码中做了很多修改,方法更加简洁,这个下面会提到。虽然代码改了很多,但是原理都是一样的,我们只需要重写不同的方法而已。

默认认证

首先我们使用Laravel 5.3提供的开箱即用的认证:

php artisan make:auth

该Artisan命令会生成用户认证所需的路由、视图以及HomeController:

认证的路由也一并生成好了,查看路由文件routes/web.php,会发现该文件已经被更新:

Auth::routes();

Route::get('/home', 'HomeController@index');

其中Auth::routes()定义了登录注册及找回密码路由,/home 为用户认证成功后跳转的路由。

验证

接下来我们先实现前台用户登录,也就是Laravel自带的Users用户表登录。通过生成的默认登录认证,已经写好了所有代码,剩下要做的就是使用迁移命令创建用户认证相关表:

php artisan migrate

执行命令后会生成 users 表和 password_resets 表,分别为用户表和密码重置表。然后我们就可以在浏览器中输入http://blog.me/register来注册新用户:

我们创建一个 iwanli 的用户,注册成功后直接跳转 /home,并且刚注册的用户名也已经显示出来了:

登录、找回密码功能都已经写好,我就不一一测试了~

自定义用户表登录

首先要看看默认的用户认证配置文件auth.php,配置如下:

<?php
return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
    ],
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'email' => 'auth.emails.password',
            'table' => 'password_resets',
           'expire' => 60,
        ],
    ],
];

认证是由 guardprovider 两部分构成的(参考用户认证文档),defaults 配置是选择哪一个 guard 认证驱动,所以我们在这两个配置项中分别添加一个 adminadmins 选项。

<?php
return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],
    ],
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'email' => 'auth.emails.password',
            'table' => 'password_resets',
           'expire' => 60,
        ],
    ],
];

创建后台用户用户表及Model

接下来我们来实现后台用户登录,使用如下Artisan命令生成后台用户Model:

php artisan make:model Models/Admin -m

带上-m 选项会生成对应迁移文件 *_create_admins_table,我们定义该数据表字段和users一样,你也可以自定义:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateAdminsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('admins');
    }
}

由于后台一般只需要登录功能,所以来给 admins 表填充一些数据:

php artisan make:seeder AdminsTableSeeder

执行完命令后将会在 database/seeds 目录下生成 AdminsTableSeeder.php 文件。接下来我们定义一个数据模型工厂,在 database/factories/ModelFactory.php 中添加如下代码:

<?php

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| Here you may define all of your model factories. Model factories give
| you a convenient way to create models for testing and seeding your
| database. Just tell the factory how a default model should look.
|
*/

$factory->define(App\User::class, function (Faker\Generator $faker) {
    static $password;

    return [
        'name' => $faker->name,
        'email' => $faker->safeEmail,
        'password' => $password ?: $password = bcrypt('secret'),
        'remember_token' => str_random(10),
    ];
});

$factory->define(App\Models\Admin::class, function (Faker\Generator $faker) {
    static $password;

    return [
        'name' => $faker->name,
        'email' => $faker->safeEmail,
        'password' => $password ?: $password = bcrypt('secret'),
        'remember_token' => str_random(10),
    ];
});

模型工厂定义完成后,在 AdminsTableSeeder.php 中填充数据:

<?php

use Illuminate\Database\Seeder;

class AdminsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory('App\Models\Admin',3)->create([
            'password' => bcrypt('123456')
            ]);
    }
}

填充数据弄好后,在 DatabaseSeeder.php 中加入 AdminsTableSeeder

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // $this->call(UsersTableSeeder::class);
        $this->call(AdminsTableSeeder::class);
    }
}

最后执行迁移命令:

php artisan migrate --seed

OK,我们在查看数据库:

修改Admin模型类如下:

<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

后台用户认证路由及控制器

使用Artisan命令创建控制器:

php artisan make:controller Admin/LoginController
php artisan make:controller Admin/DashboardController

编辑 Admin/LoginController.php ,代码如下:

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
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 / registration.
     *
     * @var string
     */
    protected $redirectTo = '/admin/dash';
    protected $username;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest:admin', ['except' => 'logout']);
        $this->username = config('admin.global.username');
    }
    /**
     * 重写登录视图页面
     * @author 晚黎
     * @date   2016-09-05T23:06:16+0800
     * @return [type]                   [description]
     */
    public function showLoginForm()
    {
        return view('admin.login.index');
    }
    /**
     * 自定义认证驱动
     * @author 晚黎
     * @date   2016-09-05T23:53:07+0800
     * @return [type]                   [description]
     */
    protected function guard()
    {
        return auth()->guard('admin');
    }
}

LoginController 中我们在构造函数中修改了 guest 中间件,用来跳转不同路由:

  • app\Http\Middleware\RedirectIfAuthenticated.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->check()) {
            // 根据不同 guard 跳转到不同的页面
            $url = $guard ? 'admin/dash':'/home';
            return redirect($url);
        }

        return $next($request);
    }
}

编辑 Admin/DashboardController.php ,代码如下:

<?php

namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;

class DashboardController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth.admin:admin');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        dd('后台首页,当前用户名:'.auth('admin')->user()->name);
    }
}

DashboardController 构造函数中我们添加了一个 auth.admin Middleware,这个是我们自定义的,所以我们要来新建一个 Middleware

php artisan make:middleware AdminAuthMiddleware

编辑 AdminAuthMiddleware

<?php

namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class AdminAuthMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next,$guard = null)
    {
        if (Auth::guard($guard)->guest()) {
            if ($request->ajax() || $request->wantsJson()) {
                return response('Unauthorized.', 401);
            } else {
                return redirect()->guest('admin/login');
            }
        }
        return $next($request);
    }
}

app\Http\Kernel.php 中注册:

protected $routeMiddleware = [
    ......
    'auth.admin' => \App\Http\Middleware\AdminAuthMiddleware::class,
    ......
];

routes/web.php 中添加如下路由:

Route::group(['prefix' => 'admin','namespace' => 'Admin'],function ($router)
{
    $router->get('login', 'LoginController@showLoginForm')->name('admin.login');
    $router->post('login', 'LoginController@login');
    $router->post('logout', 'LoginController@logout');

    $router->get('dash', 'DashboardController@index');
});

视图文件创建及修改

最后我们要创建后台用户认证对应视图文件,这里我们简单复制默认用户视图模板并稍作修改即可,复制 resources\views\auth\login.blade.php 文件到并重命名 resources\views\admin\login\index.blade.php

修改resources\views\admin\login\index.blade.php目录下登录及注册表单提交地址:

{{ url('/login') }} -> {{ route('admin.login') }}

OK,在浏览器中访问http://blog.me/admin/login 测试:

点击login,页面跳转到http://blog.me/admin/dash,说明后台登录成功!

OK,至此我们已经完成前后台用户同时登录认证功能。 Enjoy it !

更多关于Laravel的文章请关注我的博客:http://www.iwanli.me

本帖已被设为精华帖!
本帖由 Summer 于 7年前 加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 58

@lanceWan 感谢分享这么高质量的文章,对于新手与我来讲都有很大的帮助,感谢!另外帮忙改了一处:routes\web.php -> routes/web.php, 加油!

7年前 评论
Summer

这是 5.3 默认的登录注册页面 UI 吗?阿黎

7年前 评论

前台和后台session信息该怎么区分呀

4年前 评论

发现一个不知道算不算问题的问题,

就是如果同时登录了多个用户(不同表),只要其中一个用户退出,所有用户都退出了,,

注释掉AuthenticatesUserslogout方法里的$request->session()->invalidate();这一行,可以只退出你要退出的用户,就是不知道有没有什么后遗症?

或者有没有方法可以清除特定guard的session?

望大佬解答~~

6年前 评论
yujiarong 4年前
largezhou (作者) 4年前
幽弥狂

错别字 创建后台login控制器命令的 打错字了

7年前 评论

@lanceWan 是添加路由的时候没有在web中间件加,不能处理$error全局变量,现在好了,谢啦!!☆⌒(*^-゜)v

7年前 评论

@贺钧威 没有什么不妥的,对你有帮助就行了~

7年前 评论

参考您的文章,进行了拓展,教程:Laravel5.3 多表验证(包括登录注册,找回密码,发送邮件) 里边提到了您的文章,感觉不妥的话可以联系我~

7年前 评论

@JINJIALEI 注销这个问题的话我还没仔细去看Laravel的方法,有时间我去仔细看看有什么方法解决

7年前 评论

@周军 谢谢 原谅总打错字:smile:

7年前 评论

logout的时候为什么直接跳转到 前台的首页,而且后台注销后前台也注销了,前台注销后台也注销了,怎么修改才能前后台各自可以注销

7年前 评论

php artisan make:controller Admin/LgoinController

的单词拼写错了
应该是:
···
php artisan make:controller Admin/LoginController
···

7年前 评论

@shawn 恩 这个中间件也是指定配置中的驱动

7年前 评论

@lanceWan 就是说这个跟在控制器构造函数中指定是一样的作用是吧

public function __construction(){
    $this->middleware('auth:admin');
}
7年前 评论

@shawn 这是指定 config/auth.php 中的用那个providers进行认证 默认是使用 users的provider

7年前 评论

请问LoginController中的自定义认证驱动是什么作用?

protected function guard(){
    return auth()->guard('admin');
}
7年前 评论

我今天研究了下,感觉还是挺麻烦。假如一个系统分前台后台,甚至有些还有第三种类型,操作起来就挺麻烦。laravel这个适合一个应用只做一种身份认证。

4年前 评论

@xhh110 这个很尴尬,之前有人跟我说了 。 你们知道就行,我就不多说了~~:smile:

7年前 评论
幽弥狂

@lanceWan 可以改一下嘛

7年前 评论
幽弥狂

@lanceWan 在blade模板中。。。Auth::User()->name 获取到前台用户名称 后台怎么获取呢??
还有 Auth::guest() 怎么认证后台的呢???

7年前 评论

为什么Auth::user()取不到数据?

7年前 评论

你这样做了后,用户数据存在哪里,session中也没有

7年前 评论

@xhh110
@黄爽 要先指定 guard(非 default 的)用 Auth::guard('admin')->user()

6年前 评论
幽弥狂

@qqjt 这个我会了。就是attempt不能通过无密码认证。。。只能用setLoginId() 来实现我想要的了、。。

6年前 评论
elesos

不错,实践成功!

6年前 评论
leung0826

@largezhou $request->session()->forget('xx') 就可以了

5年前 评论

阿黎,我是完全按照你的这个流程写的,到最后点击登录提交的时候,提示我页面无法运作,没有做跳转,还停留在admin/login,能大概知道是什么原因吗?

7年前 评论

@overtrue :smile: 非常感谢~

7年前 评论
ruolis

大赞

7年前 评论

这篇文章对新手很有学习参考价值 :+1:

7年前 评论

很厉害,写的这么详细。:+1:

7年前 评论

犹如行云流水一般,楼主博客好漂亮

7年前 评论
Summer

标题错别字 -> Laravl5.3 我已经改过来,博客的你自己改哈

7年前 评论

@Summer 。。。。。看来每次写完要查几次错别字了

7年前 评论
Summer

@lanceWan 哈哈,没事,这种事情经常发生

7年前 评论

还是放一起的好,最好不好区分

7年前 评论
Destiny

:punch: 非常有价值!

7年前 评论

直接用users表然后给权限不就行了吗

7年前 评论

@ilhamxyz 方法有很多种,这只是其中的一种方案。

7年前 评论

@王杰 这个可能是session的问题,你的路由加了中间件web没? 试一下

7年前 评论

@DolphinBay 没有截图看看? 这个我也不好直接说哪里问题呀

7年前 评论

file 点击登录以后就是这样,难道是表单提价路由的问题。因为有的地方的代码我还不能完全理解。 麻烦了。

7年前 评论

@lanceWan 刚才的问题是我写错了一个地方,但是又遇到一个问题就是,点击登录以后确实能跳转到,admin/dash 但是会提示页面无法运作,代码追到AdminAuthMiddleware.php中的handle,就挂掉了。 我按照图片的写法,就可以了。 什么愿意呢?

file

7年前 评论

@DolphinBay Auth是门面,必须 use Auth 一下才能用 ,或者想你那样加个 \ 就可以了 。或者你还可以用辅助函数 auth($guard)->guest() 也是一样的效果

7年前 评论

@lanceWan 非常感谢,已经应用到项目中了。虽然还有个别地方不理解。

7年前 评论

@lanceWan 在修改Admin模型那里,use了这个类,我不能理解,是否可以解释一下?卡在这里好久了
use Illuminate\Foundation\Auth\User as Authenticatable;
还下面的继承关系
class Admin extends Authenticatable

7年前 评论

博主写的非常赞

7年前 评论

@lanceWan 还有一个问题请教一下,如果我没有登陆,范文会自动跳转到 XX.com/admin/login, 但是我登陆了以后, 在去访问 xx.com/admin/login,就会跳转到, xx.com/admin/dash 。然后报错,报的是没有admin/dash路由的问题,可是我登陆成功明显跳转的不是这个路由,而且 ,我的代码里面根本就没有 dash 这个关键字,是个是为什么啊 ?

7年前 评论

@ChenPHPER class Admin extends Authenticatable 这个之所以要继承这个类,是因为Laravel认证驱动会检查你的Model必须继承这个类 ,不然会报错。 Authenticatable 实质上还是继承model的,只不过多了一些方法

7年前 评论

还有个问题,use Illuminate\Foundation\Auth\User as Authenticatable;在这里,我model里面是没有User这个模型的,相对应的用户的是Member,这个需要修改什么吗?

7年前 评论

@ChenPHPER 我想你理解错了,这个User并不是你认为的users表,代表的是用户管理吧。这是Laravel的源码:

<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

class User extends Model implements
    AuthenticatableContract,
    AuthorizableContract,
    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword;
}

源码里面实质还是继承model,只不过实现了一些接口而已。实现接口方法由 Authenticatable, Authorizable, CanResetPassword 这三个 Trait实现,分别处理认证用户,修改密码之类的。所以你不用修改任何东西,只要你继承就行

7年前 评论

@lanceWan ok,明白了。我之前只看了下api文档,没搞明白,现在点通了,谢谢哈~不过我们不是基于jwt认证的吗?为什么还要用laravel底层的东西去实现?

7年前 评论

博主我复制login.blade.php文件后改名为admin目录下login目录下的index.blade.app后访问页面就会报$error这个全局变量没定义,而访问login.blade.php却没有报这个错
附上报错的图

file

7年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!