Laravel支持基于多表的用户认证,同时允许不同的数据表用户(前/后台)进行登录验证
在上一篇记录,我们完成了前台用户的认证,在这里只做后台用户登录认证
1.创建后台用户模型,并生成表
php artisan make:model Admin -m
编辑迁移文件设置表结构
2.编辑Admin模型
将User模型的代码复制到Admin模型
<?php
namespace App;
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',
];
}
3.编辑认证配置文件
要实现多表用户认证,首先要配置认证配置文件auth.php
<?php
return [
//默认用户认证配置,即不指定特定认证服务方的话,使用以下默认配置
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
//在这里配置不同的认证服务方,默认支持web和api认证
//即web路由的请求认证和api路由的请求认证
//如果要配置其他的认证服务方,如后台登录,需要在这里配置
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
//在这里配置系统支持的认证提供者(用户数据来源)
//默认基于User模型的EloquentProvider
//如果系统支持不同表用户登录,需要在这里额外配置
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
//密码重置表,默认支持user表的密码重置,对应的数据表是password_resets
//如果要支持其他用户表的密码重置,需要在这里额外配置
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],
];
根据上面的配置项完成auth
的配置,我们默认使用的web
guard实现前台登录,要实现后台登录我们需要在guards数组中添加一个admin
guard,参照’web’
如:
'guards'=>[
'web'=>[
'driver'=>'session',
'provider'=>'users',
],
'admin'=>[
'driver'=>'session',
'provider'=>'admins'
]
'api'=>[
'driver'=>'token',
'provider'=>'users',
],
],
在providers中新增一个后台用户数据库提供者admins
'providers'=> [
'users'=>[
'driver'=>'eloquent',
'model'=>App\User::class,
],
'admins'=>[
'driver'=>'eloquent',
'model'=>App\Admin::class,
],
],
密码重置表在这里由于是后台用户暂不设置
完整的auth
.php
<?php
return [
//默认用户认证配置,即不指定特定认证服务方的话,使用以下默认配置
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
//在这里配置不同的认证服务方,默认支持web和api认证
//即web路由的请求认证和api路由的请求认证
//如果要配置其他的认证服务方,如后台登录,需要在这里配置
'guards'=>[
'web'=>[
'driver'=>'session',
'provider'=>'users',
],
'admin'=>[
'driver'=>'session',
'provider'=>'admins'
],
'api'=>[
'driver'=>'token',
'provider'=>'users',
],
],
//在这里配置系统支持的认证提供者(用户数据来源)
//默认基于User模型的EloquentProvider
//如果系统支持不同表用户登录,需要在这里额外配置
'providers'=> [
'users'=>[
'driver'=>'eloquent',
'model'=>App\User::class,
],
'admins'=>[
'driver'=>'eloquent',
'model'=>App\Admin::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
//密码重置表,默认支持user表的密码重置,对应的数据表是password_resets
//如果要支持其他用户表的密码重置,需要在这里额外配置
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],
];
4.定义后台用户认证路由及控制器
4.1增加如下路由
//后台用户登录
Route::get('admin/login','Admin\LoginController@showLoginForm')->name('admin.login');
//后台用户登录界面
Route::post('admin/login','Admin\LoginController@login');
//后台用户登录逻辑
Route::get('admin/register','Admin\RegisterController@showRegistrationForm')->name('admin.register');
//后台用户注册界面
Route::post('admin/register','Admin\RegisterController@register');
//后台用户注册逻辑
Route::post('admin/logout','Admin\LoginController@logout')->name('admin.logout');
//退出登录
Route::get('admin','AdminController@index')->name('admin.home');
//后台管理界面
4.2创建后台认证控制器
php artisan make:controller Admin/LoginController
php artisan make:controller Admin/RegisterController
php artisan make:controller AdminController
4.3编辑Admin/LoginController.php
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo = '/admin';
public function __construct()
{
$this->middleware('guest:admin')->except('logout');
}
//展示后台登录页面
public function showLoginForm()
{
return view('admin.login');
}
//传入认证服务方配置
protected function guard()
{
return Auth::guard('admin');
}
//退出后跳转后台登录页面
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->invalidate();
return redirect('admin/login');
}
}
说明:在LoginController中我们重写了AuthenticatesUsers
中的两个方法,showlLoginForm()
用于显示用户后台登录界面,guuard
方法用于在Auth::guard
方法中传入对应的认证服务方配置并将其返回,这样使用后台登录时,使用的就是上面配置的后台认证guard
了,该参数默认值是web
(在auth.php
的defaults
)中配置,所以我们在前台用户认证时不需要手动传入。
由于我们在中间件guest
中也传入了admin
参数,表示判断是否为后台登录,所以我们还要修改guest
中间件对应类RedirectIfAuthenticated
的处理办法—handle
,当传入guard
是admin
时,跳转后台主页
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
if($guard == 'admin'){
return redirect('/admin');//后台用户登录时跳转后台主页
}
return redirect('/home');
}
return $next($request);
}
同时我们还要编辑后台用户注册类Admin\RegisterController.php
<?php
namespace App\Http\Controllers\Admin;
use App\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = '/admin';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:admin');
}
public function showRegistrationForm()
{
return view('admin.register');
//后台注册页面
}
protected function guard()
{
//传入认证服务方配置
return Auth::guard('admin');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\User
*/
protected function create(array $data)
{
return Admin::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
}
4.4编辑AdminController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AdminController extends Controller
{
//
public function __construct()
{
$this->middleware('auth:admin');
}
public function index(){
return view('admin.home');
}
}
通过index
方法渲染登录后的管理页面,如果没有登录,访问该方法会通过auth
中间件进行处理,我们在中间件中也传入了admin
参数,用于判断后台是否登录,同样我们还要修改auth
中间件对应类Authenticate
来处理后台未登录的跳转 如果没有需要我们手动创建
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
protected $redirectTo = '';
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string
*/
protected function redirectTo($request)
{
return route('login');
}
protected function authenticate($request, array $guards)
{
if (empty($guards)) {
$guards = [null];
}
foreach ($guards as $guard) {
if ($this->auth->guard($guard)->check()) {
return $this->auth->shouldUse($guard);
}
}
// 这里我们以 guards 传入的第一个参数为准选择跳转到的登录页面
$guard = $guards[0];
if ($guard == 'admin') {
$this->redirectTo = route('admin.login');
}
throw new AuthenticationException(
'Unauthenticated.', $guards, $this->redirectTo ? : $this->redirectTo($request)
);
}
}
5.视图文件创建及修改
5.1创建后台视图,创建后台视图文件之前我们需要先为后台认证创建一个布局文件
复制layouts\app.blade.php
进行编写
创建admin.blade.php
布局文件
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }} Admin</title>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
<!-- Fonts -->
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<nav class="navbar navbar-expand-md navbar-light navbar-laravel">
<div class="container">
<a class="navbar-brand" href="{{ url('/admin') }}">
{{ config('app.name', 'Laravel') }} Admin
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Left Side Of Navbar -->
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="{{ url('/') }}">Home</a>
</li>
</ul>
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Authentication Links -->
@guest
<li class="nav-item">
<a class="nav-link" href="{{ route('admin.login') }}">{{ __('Login') }}</a>
</li>
<li class="nav-item">
@if (Route::has('admin.register'))
<a class="nav-link" href="{{ route('admin.register') }}">{{ __('Register') }}</a>
@endif
</li>
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user('admin')->name }} <span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('admin.logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('admin.logout') }}" method="POST" style="display: none;">
@csrf
</form>
</div>
</li>
@endguest
</ul>
</div>
</div>
</nav>
<main class="py-4">
@yield('content')
</main>
</div>
</body>
</html>
5.2创建后台认证视图
创建后台认证视图,先在rescources/views
目录下创建admin
子目录,然后将前台用户认证模板复制并修改即可
login页面
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Login</div>
<div class="panel-body">
<form class="form-horizontal" method="POST" action="{{ route('admin.login') }}">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email" class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input id="email" type="text" class="form-control" name="name" value="{{ old('email') }}" required autofocus>
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label for="password" class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control" name="password" required>
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<div class="checkbox">
<label>
<input type="checkbox" name="remember" {{ old('remember') ? 'checked' : '' }}> Remember Me
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-8 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Login
</button>
<a class="btn btn-link" href="{{ route('password.request') }}">
Forgot Your Password?
</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
register
页面
@extends('layouts.admin')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Admin Register') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('admin.register') }}">
{{csrf_field()}}
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control{{ $errors->has('name') ? ' is-invalid' : '' }}" name="name" value="{{ old('name') }}" required autofocus>
@if ($errors->has('name'))
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('name') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required>
@if ($errors->has('email'))
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>
@if ($errors->has('password'))
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Register') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
home
页面
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Admin Dashboard</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
欢迎登录管理员
</div>
</div>
</div>
</div>
</div>
@endsection
登录效果(管理员)
登录效果(普通用户)