laravel auth.php,Laravel 用户认证 Auth

韦高谊
2023-12-01

很多应用是需要登陆后才能操作,Laravel提供了一个auth工具来实现用户的认证功能。并且有一个config/auth.php来配置auth工具。大概看一下auth工具的常用方法

Auth::check();// 判断当前用户是否未登录

Auth::guest();// 判断当前用户是否未登录,与 check() 相反

Auth::guard();// 自定义看守器 默认为 `web`

Auth::user();// 获取当前的认证用户,一个提供者的模型

Auth::id();// 获取当前的认证用户的 ID(未登录情况下会报错)

Auth::attempt(['email' => $email, 'password' => $password],true);// 通过给定的信息来尝试对用户进行认证(成功后会自动启动会话),第一个数组就是认证的参数,第二个参数true就是'记住我'功能

Auth::login(User::find(1), $remember = false);// 登录一个指定用户到应用上,一般是登陆的参数通过后,执行login方法,保存session等登陆成功的操作

Auth::logout();// 使用户退出登录(清除会话)

看一下auth.php的配置,其实Auth的实现原理就是通过一个guard来实现的

'defaults' => [

'guard' => 'web', //没有指定guard时,就用‘web’

'passwords' => 'users',

],

'guards' => [ //这就是guard数组,用哪一个guard需要指定

'web' => [

'driver' => 'session',

'provider' => 'users',

],

'api' => [

'driver' => 'token',

'provider' => 'users',

'hash' => false,

],

],

//每一个guard最少需要两个成员,driver驱动(比如登陆成功,通过什么方式保存登陆状态),provider提供者(就是用户表,保存用户名密码的那张表)

所以一个系统中,我们可以有多个认证体系,前后台认证,接口认证等等,只要配置不同的guard即可。不同认证体系,使用对应的guard就可以。

说了这么多,还是很懵逼,不怕,laravel给我们实现一个客户认证脚手架,通过命令就可以实现一套认证系统。我们把这套系统弄懂,就可以仿照它的体系生成我们自己的认证体系。

laravel脚手架实现认证体系

php artisan make:auth

这个命令会做哪些动作?

1.生成登陆,注册,退出等等的路由,在web.php新增Auth::routes();等价于下面

public function auth(array $options = [])

{

// Authentication Routes...

$this->get('login', 'Auth\LoginController@showLoginForm')->name('login');

$this->post('login', 'Auth\LoginController@login');

$this->post('logout', 'Auth\LoginController@logout')->name('logout');

// Registration Routes...

if ($options['register'] ?? true) {

$this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');

$this->post('register', 'Auth\RegisterController@register');

}

// Password Reset Routes...

if ($options['reset'] ?? true) {

$this->resetPassword();

}

// Email Verification Routes...

if ($options['verify'] ?? false) {

$this->emailVerification();

}

}

其实就到调用Router::auth(),我们就可以看到登陆注册的路由啦,其他功能类似。

2.生成登陆注册等控制器App\Http\Controllers\Auth目录下面,上面的路由就是访问这里的控制器

3.生成登陆注册等页面resource/views/auth/目录下

4.生成provider表的migerate,创建用户表,这时候你可以修改这个用户表字段

public function up()

{

Schema::create('users', function (Blueprint $table) {

$table->bigIncrements('id');

$table->string('name');

$table->string('email')->unique();

$table->timestamp('email_verified_at')->nullable();

$table->string('password');

$table->rememberToken();

$table->timestamps();

});

}

php artisan migrate//生成user表

这样,访问上面的路由就可以生成一套认证体系了。认证系统其实就是三个步骤

1.登陆注册等逻辑接口,就是控制器

2.配置路由

3.页面

脚手架认证体系原理,我们自定义认证体系

先看注册的逻辑,我们自定义自己的注册类的时候,也可以参考着写,跳转路径,检查字段,用户表都可以根据实际情况修改。而接口,laravel已经写在RegistersUsers里面了。

class RegisterController extends Controller

{

use RegistersUsers; //这个是laravel写好的trait

protected $redirectTo = '/home';//注册成功跳转路径

public function __construct()

{

$this->middleware('guest');//添加一个guest中间件

}

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:8', 'confirmed'],

]);

}

protected function create(array $data)

{

return User::create([ //注册最终会写入到provider中,通过create插入数据,返回模型

'name' => $data['name'],

'email' => $data['email'],

'password' => Hash::make($data['password']),

]);

}

protected function guard()//这个方法trait也有,但是如果我们用其他的guard,就要重写方法

{

return Auth::guard('api');//你要使用的guard

}

}

但是还是要看一下laravel是怎么通过auth和auth的配置来实现注册的

public function register(Request $request)

{

$this->validator($request->all())->validate();//判断参数

event(new Registered($user = $this->create($request->all())));//注意看create方法,这是插入到用户表中,返回那个模型。

$this->guard()->login($user);//获取对应的guard,执行login方法,其实就是告诉session这个用户登录成功状态。(注册成功,就不用重新登录),具体流程下面登录成功的操作再分析

return $this->registered($request, $user)

?: redirect($this->redirectPath());//注册成功跳转

}

其实注册步骤还是非常简单的,参数正确,就可以写入用户表,注册成功,然后跳转。

登录逻辑

class LoginController extends Controller

{

use AuthenticatesUsers;

protected $redirectTo = '/home';//登录成功跳转路径

public function __construct()

{

$this->middleware('guest')->except('logout');//添加guest中间件,除了logout方法

}

protected function guard()//这个方法trait也有,但是如果我们用其他的guard,就要重写方法

{

return Auth::guard('api');//你要使用的guard

}

}

登录接口laravel也帮我们实现了,直接use AuthenticatesUsers.我们看看

public function login(Request $request)

{

$this->validateLogin($request);//验证参数

if ($this->hasTooManyLoginAttempts($request)) {//登录失败次数,超过次数不能再登陆

$this->fireLockoutEvent($request);

return $this->sendLockoutResponse($request);

}

if ($this->attemptLogin($request)) { //比对数据库,看看登陆是否成功

return $this->sendLoginResponse($request);

}

$this->incrementLoginAttempts($request);//增加登陆失败次数,以$this->username())),$request->ip()为基准,就是说同一个username,同一个ip登陆失败次数是有限的。时间是1h.

return $this->sendFailedLoginResponse($request);

}

我们主要看他如何查用户表,如何判断登录是否成功

protected function attemptLogin(Request $request)

{

return $this->guard()->attempt(

$this->credentials($request), $request->filled('remember')

);

}

和注册一样,也是获取到对应的guard配置的Auth,执行attempt方法来验证,所以不同的guard就要重写guard方法,才能配置Auth.

Auth门面介绍

其实操作的类是Illuminate\Auth\AuthManager,而最终功能其实是对应的driver类实现的。

Auth::guard('web'),返回一个对象,就是那个driver对象

public function guard($name = null)

{

$name = $name ?: $this->getDefaultDriver();

return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);

}

protected function resolve($name)

{

$config = $this->getConfig($name);

if (is_null($config)) {

throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");

}

if (isset($this->customCreators[$config['driver']])) {

return $this->callCustomCreator($name, $config);

}

$driverMethod = 'create'.ucfirst($config['driver']).'Driver';

if (method_exists($this, $driverMethod)) {

return $this->{$driverMethod}($name, $config);

}

throw new InvalidArgumentException(

"Auth driver [{$config['driver']}] for guard [{$name}] is not defined."

);

}

需要说明的,laravel给我们配置了三种guard的driver,分别是session,token,request,都放在Illuminate\Auth目录下,先看一下SessionGuard

public function __construct($name,UserProvider $provider,Session $session,Request rquest = null)

{

$this->name = $name;

$this->session = $session;//操作session的工具

$this->request = $request;//request

$this->provider = $provider;//就是我们的provider模型

}

有了这三个工具,我们就可以实现auth的各种功能了。

比如登陆attempt()

public function attempt(array $credentials = [], $remember = false)

{

$this->fireAttemptEvent($credentials, $remember);

//用除了密码查询provider表,得到$user。

$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);

//对比密码是否正确

if ($this->hasValidCredentials($user, $credentials)) {//严重

$this->login($user, $remember);//成功就执行login方法

return true;

}

$this->fireFailedEvent($user, $credentials);

return false;

}

比如获取登陆的客户模型Auth::user();

public function user()

{

if ($this->loggedOut) {

return;

}

if (! is_null($this->user)) {

return $this->user;

}

$id = $this->session->get($this->getName());//通过session获取id

if (! is_null($id) && $this->user = $this->provider->retrieveById($id)) {

$this->fireAuthenticatedEvent($this->user);

}

if (is_null($this->user) && ! is_null($recaller = $this->recaller())) {

$this->user = $this->userFromRecaller($recaller);

if ($this->user) {

$this->updateSession($this->user->getAuthIdentifier());

$this->fireLoginEvent($this->user, true);

}

}

return $this->user;

}

在看看TokenGuard,就是登陆成功会给前端保存一个token,每次请求要带这个token

Auth::user();

public function user()

{

// If we've already retrieved the user for the current request we can just

// return it back immediately. We do not want to fetch the user data on

// every call to this method because that would be tremendously slow.

if (! is_null($this->user)) {

return $this->user;

}

$user = null;

$token = $this->getTokenForRequest();//从request得到token值

if (! empty($token)) {

$user = $this->provider->retrieveByCredentials([

$this->storageKey => $this->hash ? hash('sha256', $token) : $token,

]);

}

return $this->user = $user;

}

$this->storageKey 默认是"api_token",所以查询provider表的条件是['api_token'=>$token],我们的用户表需要一个api_token字段保存登陆的token值。

Auth中间件验证登陆

我们知道,要让接口通过登陆验证才能访问,需要在添加auth中间件,这个laravel已经配置好了

protected $routeMiddleware = [

'auth' => \App\Http\Middleware\Authenticate::class,//系统生成的前台登陆验证

'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,

'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,

'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,

'can' => \Illuminate\Auth\Middleware\Authorize::class,

'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,

'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,

'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,

'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,

'auth.admin' => \App\Http\Middleware\AdminAuthMiddleware::class,// 后台登陆验证

];

这个Authenticate中间件继承了Illuminate\Auth\Middleware\Authenticate类

看它的handle方法

public function handle($request, Closure $next, ...$guards)

{

$this->authenticate($request, $guards);

return $next($request);

}

protected function authenticate($request, array $guards)

{

if (empty($guards)) {

$guards = [null];

}

foreach ($guards as $guard) {

if ($this->auth->guard($guard)->check()) { //Auth::check方法验证

return $this->auth->shouldUse($guard);

}

}

throw new AuthenticationException(

'Unauthenticated.', $guards, $this->redirectTo($request)

);

}

自定义后台验证如何配置验证中间件

比如我们建了一套后台的登陆验证系统,使用的guards是admin,我们的后台路由就需要一个中间件来验证登陆了

php artisan make:middleware AdminAuth

把中间件写入到路由中间件数组中

protected $routeMiddleware = [

'auth' => \App\Http\Middleware\Authenticate::class,//系统生成的前台登陆验证

'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,

'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,

'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,

'can' => \Illuminate\Auth\Middleware\Authorize::class,

'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,

'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,

'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,

'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,

'auth.admin' => \App\Http\Middleware\AdminAuthMiddleware::class,// 后台登陆验证

];

中间件的过滤代码

public function handle($request, Closure $next, $guard = null)

{

//当 auth 中间件判定某个用户未认证,会返回一个 JSON 401 响应,或者,如果是 Ajax 请求的话,将用户重定向到 login 命名路由(也就是登录页面)。

if (Auth::guard($guard)->guest()) {

if ($request->ajax() || $request->wantsJson()) {

return response('Unauthorized.', 401);

} else {

return redirect()->guest('admin/login');

}

}

return $next($request);

}

这里中间件需要传一个参数$guard,不传就是web,所以说中间件的guard要和我们的登陆,注册等系列接口要保持一致。假如我们创建了一个admin的guard,路由就应该这样写

// 后台其他页面都要有登陆验证

Route::middleware('auth.admin:admin')->name('admin.')->group(function () {

//后台的其他路由

}

本作品采用《CC 协议》,转载必须注明作者和本文链接

用过哪些工具?为啥用这个工具(速度快,支持高并发...)?底层如何实现的?

 类似资料: