Laravel5.3 多表验证(包括登录注册,找回密码,发送邮件)

主要内容

我们在开发过程中,往往会需要前台后台两个部分组成一个完整的项目,一些公司需要频繁的在短期内开发一个项目,往往这些项目的一部分功能是重复的,所以可以提炼出来做为模板,以供下个项目使用,之前有人发了登录部分的实现,今天想聊一下包括找回密码和发送邮件在内的一些细节,我们都通过 laravel 内置的验证来完成。

第一步

在一个初始的 laravel 框架中,运行这个 php artisan make:auth 来生成我们需要的基础验证路由,视图和控制器,我们初始化了一个 laravel 框架之后,运行该命令增加的文件如下。

file

更改结构,复制粘贴

我们需要将她复制为两个模块,一个是前台(以 web 命名),一个是后台(以 admin 命名)。

路由

Route::get('/', function () {
    return view('welcome');
});

Route::group([
    'namespace' => 'Web',
], function ($route) {

    $route->get('/home', 'HomeController@index');

    Auth::routes();
});

Route::group([
    'prefix'    => 'admin',
    'namespace' => 'Admin',
], function ($route) {

    Auth::routes();

    $route->get('/', 'HomeController@index');
});

视图

views 下新建两个文件夹,一个为 web 一个是 admin ,将生成的 auth 文件夹复制,扔在这两个目录下,原来的删掉就好,将 home.blade.php 放在 web 下,再复制一份放在 admin下,可以更改里边内容来区别用户和管理员界面,并且一定记得修改 admin 下的4个表单的 actionlogin 视图的 reset password 的链接,加上前缀 admin views 修改好后如下:

file

控制器

和视图类似,在 Controllers 目录下新建两个文件夹 WebAdmin,将 Auth 文件夹复制并扔在这两个文件夹里,删除原来的 Auth ,并把 HomeController.php 扔在 Web 目录下,并且复制一份扔在 Admin 并且修改这两个 HomeContrllerindex 方法里 view('web.home')view('admin.home') ,目录下此时注意修改命名空间,将这
10个控制器的 namespace 根据所在目录分别加上 WebAdmin,以 Web/Auth/LoginController.php 为例,修改命名空间为 namespace App\Http\Controllers\Web\Auth; 复制粘贴的工作就做完了,此时的控制器如下:

file

可是此时打开登录界面会产生如下错误:

file

注意,我们可以看到 LoginController 里的 showLoginForm 方法,我们回到 Web/LoginController.php 可以看到她引用了一个 trait

<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
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;

    ......

跟踪过去后是如下代码:

<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Lang;

trait AuthenticatesUsers
{
    use RedirectsUsers, ThrottlesLogins;

    /**
     * Show the application's login form.
     *
     * @return \Illuminate\Http\Response
     */
    public function showLoginForm()
    {
        return view('auth.login');
    }

    ......
}

这个 trait 很重要我们之后会覆盖这里的其他方法,此时因为我们在 views 下移走了 auth 目录,所以是错到了这里,可是这个 trait 是大家公共使用的,我们不能随便在此更改,因此需要在 LoginController 里覆盖这个方法,在 LoginController 里增加如下代码:

public function showLoginForm()
{
    return view('web.auth.login');
}

这样就成功的走到了我们更改后的目录里,同理覆盖 RegisterController.php 里的 showRegistrationForm() 方法,以及对应的 Admin 目录下的控制器方法,控制器到这里先停一下,

中间件

我们需要聊点别的。此时用户的登录注册页面可以正常打开,但是管理员的却不行,因为我们此时走的是同一个验证的中间件,所以需要一个验证身份的中间件,新建一个 middlewareAdminAuthMiddleware.php 如下:
(参考: 教程: Laravel 5.3 多用户表登录

<?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 中的数组 $routeMiddleware 注册

/**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.admin' => \App\Http\Middleware\AdminAuthMiddleware::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,
    ];

修改 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/':'/';
            return redirect($url);
        }

        return $next($request);
    }
}

更改配置文件(其中涉及的模型请继续往下看):
config/auth.conf:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option controls the default authentication "guard" and password
    | reset options for your application. You may change these defaults
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard'     => 'web',
        'passwords' => 'users',
    ],

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | here which uses session storage and the Eloquent user provider.
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | Supported: "session", "token"
    |
    */

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

        'admin' => [
            'driver'   => 'session',
            'provider' => 'admins',
        ],

        'api' => [
            'driver'   => 'token',
            'provider' => 'users',
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | If you have multiple user tables or models you may configure multiple
    | sources which represent each model / table. These sources may then
    | be assigned to any extra authentication guards you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */

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

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],

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

    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    |
    | You may specify multiple password reset configurations if you have more
    | than one user table or model in the application and you want to have
    | separate password reset settings based on the specific user types.
    |
    | The expire time is the number of minutes that the reset token should be
    | considered valid. This security feature keeps tokens short-lived so
    | they have less time to be guessed. You may change this as needed.
    |
    */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table'    => 'password_resets',
            'expire'   => 60,
        ],

        'admins' => [
            'provider' => 'admins',
            'table'    => 'admin_password_resets',
            'expire'   => 60,
        ]
    ],

];

模型

app 目录下新建 Models 目录,把 User.php 复制一份改名为 Admin.php ,注意同时修改类名为Admin ,新建数据表:

php artisan make:migration create_admins_table
php artisan make:migration create_admin_password_resets_table
php artisan make:seeder UsersTableSeeder
php artisan make:seeder AdminsTableSeeder

他们分别长这样:

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::drop('admins');
    }
}

class CreateAdminPasswordResetsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('admin_password_resets', function (Blueprint $table) {
            $table->string('email')->index();
            $table->string('token')->index();
            $table->timestamp('created_at')->nullable();
        });
    }

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

class AdminsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        \App\Models\Admin::create([
            'name' => '后台管理员',
            'email' => 'admin@admin.com',
            'password' => bcrypt('admin123'),
        ]);
    }
}

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        \App\Models\User::create([
            'name' => '普通用户',
            'email' => 'user@user.com',
            'password' => bcrypt('123456'),
        ]);
    }
}

之后迁移数据库,我们的模型就完成了,接下来继续完成控制器部分。

完善控制器

前台部分

Web/Auth/RegisterController.php 修改移了位置的 User 模型,前端登录功能不需要更改,注册功能改这里之后就完成了。

use App\Models\User;

Web/Auth/ForgotPasswordController.php

<?php

namespace App\Http\Controllers\Web\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Support\Facades\Password;

class ForgotPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset emails and
    | includes a trait which assists in sending these notifications from
    | your application to your users. Feel free to explore this trait.
    |
    */

    use SendsPasswordResetEmails;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }

    public function showLinkRequestForm()
    {
        return view('web.auth.passwords.email');
    }

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

    public function broker()
    {
        return Password::broker('users');
    }
}

Web/Auth/ResetPasswordController.php

<?php

namespace App\Http\Controllers\Web\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;

class ResetPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset requests
    | and uses a simple trait to include this behavior. You're free to
    | explore this trait and override any methods you wish to tweak.
    |
    */

    use ResetsPasswords;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }

    public function showResetForm(Request $request, $token = null)
    {
        //这里根据需求来看是否需要把邮箱直接带到页面上
        return view('web.auth.passwords.reset')->with(
            ['token' => $token, 'email' => $request->email]
        );
    }

    public function broker()
    {
        return Password::broker('users');
    }
}

.env 里配置自己的 mailtrap https://mailtrap.io以供发邮件测试,登录网站后去看他随机给的用户名密码贴上去,加密不贴:

MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=

测试用户端找回密码可用。前台大功告成~

后台部分

Admin/Auth/LoginController.php

<?php

namespace App\Http\Controllers\Admin\Auth;

use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
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.
     *
     * @var string
     */
    protected $redirectTo = '/admin';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest:admin', ['except' => [
            'logout',
            'redirectToLogin',
        ]]);
    }

    public function showLoginForm()
    {
        return view('admin.auth.login');
    }

    public function redirectToLogin()
    {
        if ($this->guard()->user()) {
            return redirect('/admin/');
        }

        return redirect('/admin/login');
    }

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

    public function logout(Request $request)
    {
        $this->guard()->logout();

        $request->session()->flush();

        $request->session()->regenerate();

        return redirect('/admin/login');
    }
}

Admin/Auth/ForgotPasswordController.php

<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Support\Facades\Password;

class ForgotPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset emails and
    | includes a trait which assists in sending these notifications from
    | your application to your users. Feel free to explore this trait.
    |
    */

    use SendsPasswordResetEmails;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }

    public function showLinkRequestForm()
    {
        return view('admin.auth.passwords.email');
    }

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

    public function broker()
    {
        return Password::broker('admins');
    }
}

邮件可以正常发送,但是邮件的内容里的链接不对啊,所以在模型 Models/Admin.php 里加入方法:

**
     * 覆盖了Authenticatable里的trait CanResetPassword的方法
     *
     * @param string $token
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new AdminResetPassword($token));
    }

然后新建 Notificationphp artisan make:notification AdminResetPassword
生成文件 app/Notifications/AdminResetPassword.php 修改内容如下:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;

class AdminResetPassword extends Notification
{
    use Queueable;

    public $token;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct($token)
    {
        $this->token = $token;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->line([
                '您收到了这份邮件,是因为我们收到了关于您账户重置密码的请求。',
                '点击下面的按钮重置密码:',
            ])
            ->action('重置密码', url('/admin/password/reset', $this->token))
            ->line('如过不是您本人操作的,请注意您的账户安全。');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

当然您也可以通过 Notification 自定义邮件视图,到此邮件可以正常发送。

Admin/Auth/ResetPasswordController.php 修改后可以正常重置密码,注册部分就不说了,一般情况后台管理员不需要注册,而且就算有业务需求,想必大家看到也能懂怎么改了。

<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;

class ResetPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset requests
    | and uses a simple trait to include this behavior. You're free to
    | explore this trait and override any methods you wish to tweak.
    |
    */

    use ResetsPasswords;

    public $redirectTo = '/admin';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }

    public function showResetForm(Request $request, $token = null)
    {
        return view('admin.auth.passwords.reset')->with(
            ['token' => $token, 'email' => $request->email]
        );
    }

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

    public function broker()
    {
        return Password::broker('admins');
    }
}

Admin/HomeController.php 在默认构造函数中进行权限判断,以助于在访问 /admin 的 url 的时候能正常跳转到 /admin/login

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class HomeController 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()
    {
        return view('admin.home');
    }
}

今天实在无聊,就写写博客啥的发泄一下。。。笔者边做边写的,理应没多大问题,其中涉及部分 laravel 的源码推荐大家可以看看,做的时候主要是 broker() 方法找不到位置,后来发现是在 app.phpproviders 数组中注册的一个 service provider Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,,不懂得可以留言,有遗漏的地方可以留言,笔者定会及时改正~

项目地址 https://github.com/horan-geeker/laravel5.3...

本帖已被设为精华帖!
本帖由系统于 2年前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!