Laravel Passport认证-多表、多字段解决方案

news/2024/11/23 3:44:28/

1. 概述

API 通常使用令牌(token)进行认证并且在请求之间不维护会话(Session)状态。Laravel 官方扩展包 Laravel Passport 让 API 认证变得轻而易举,Passport 基于 Alex Bilbie 维护的 League OAuth2 server,可以在数分钟内为 Laravel 应用提供完整的 OAuth2 服务器实现。本文主要讲述Oauth2 的'grant_type' => 'password'密码授权来做Auth验证

2. 单表用户登录

2.1 安装

首先通过 Composer 包管理器安装 Passport:

根据laravel不同的版本,加载不同的管理包,如果你使用laravel5.4版本;使用命令composer require laravel/passport v4.*;不然可能因为版本问题,加载失败

composer require laravel/passport

成功安装Passport包之后,我们需要设置他们的服务提供者。所以,打开你的config / app.php文件,并在其中添加以下提供程序。

'providers' => [....Laravel\Passport\PassportServiceProvider::class,
],

2.2 迁移数据库

Passport 服务提供者为框架注册了自己的数据库迁移目录,所以在注册服务提供者之后(Laravel 5.5之后会自动注册服务提供者)需要迁移数据库,Passport 迁移将会为应用生成用于存放客户端和访问令牌的数据表:

php artisan migrate

2.3 生成加密键oauth_clients

php artisan passport:install

该命令将会创建生成安全访问令牌(token)所需的加密键,此外,该命令还会创建“personal access”和“password grant”客户端用于生成访问令牌,生成记录存放在数据表 oauth_clients

2.4 修改user模型

添加 Laravel\Passport\HasApiTokens trait 到 App\User 模型,该 trait 将会为模型类提供一些辅助函数用于检查认证用户的 token 和 scope

<?phpnamespace App;use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;class User extends Authenticatable
{use HasApiTokens, Notifiable;
}
2.4.1 重置验证字段

先看passport封装源码,然后根据自己需求更改我们的配置;追踪源码如下

<?phpnamespace Laravel\Passport\Bridge;use RuntimeException;
use Illuminate\Contracts\Hashing\Hasher;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;class UserRepository implements UserRepositoryInterface
{/*** The hasher implementation.** @var \Illuminate\Contracts\Hashing\Hasher*/protected $hasher;/*** Create a new repository instance.** @param  \Illuminate\Contracts\Hashing\Hasher  $hasher* @return void*/public function __construct(Hasher $hasher){$this->hasher = $hasher;}/*** {@inheritdoc}*/public function getUserEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity){$provider = config('auth.guards.api.provider');if (is_null($model = config('auth.providers.'.$provider.'.model'))) {throw new RuntimeException('Unable to determine authentication model from configuration.');}if (method_exists($model, 'findForPassport')) {$user = (new $model)->findForPassport($username);} else {$user = (new $model)->where('email', $username)->first();}if (! $user) {return;} elseif (method_exists($user, 'validateForPassportPasswordGrant')) {if (! $user->validateForPassportPasswordGrant($password)) {return;}} elseif (! $this->hasher->check($password, $user->getAuthPassword())) {return;}return new User($user->getAuthIdentifier());}
}

(1)重置验证username字段,如果密码不需要重置,则不用管一下代码;
由源码method_exists($model, 'findForPassport') 我们如果要重置,只需要在User模型中添加findForPassport方法即可
默认验证email字段,如果你想验证phoneemail一起验证;在User表中添加如下方法:

 public function findForPassport($username){return $this->orWhere('email', $username)->orWhere('phone', $username)->first();}

(2)重置验证password字段,如果密码不需要重置,则不用管一下代码;
由上面源码method_exists($user, 'validateForPassportPasswordGrant')我们可以知道,只需要在我们的User模型中添加validateForPassportPasswordGrant方法就好了;代码如下

 public function validateForPassportPasswordGrant($password){//如果请求密码等于数据库密码 返回true(此为实例,根据自己需求更改)if($password == $this->password){return true;}return false;}

2.5 注册获取Token路由

接下来,需要在 AuthServiceProvider 的 boot 方法中调用 Passport::routes 、enableImplicitGrant、tokensCan、tokensExpireIn、refreshTokensExpireIn具体作用看注释

<?phpnamespace App\Providers;use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;class AuthServiceProvider extends ServiceProvider
{/*** The policy mappings for the application.** @var array*/protected $policies = ['App\Model' => 'App\Policies\ModelPolicy',];/*** Register any authentication / authorization services.** @return void*/public function boot(){$this->registerPolicies();Passport::routes();// accessToken有效期Passport::tokensExpireIn(Carbon::now()->addDays(15)); // accessRefushToken有效期Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));}
}

2.6 修改项目auth配置文件

文件位置:congig/auth.php 接口使用api, 保护项( driver )改为 passport 。此调整会让你的应用程序在接收到 API 的授权请求时使用 Passport 的 TokenGuard 来处理:

return [.....'guards' => [...'api' => ['driver' => 'passport','provider' => 'users',],],.....
]

2.7 配置完成,测试使用

个人为了方便使用,封装一个工具类来进行token测试;个人放在App\Helpers 文件夹下;代码如下:

<?phpnamespace App\Helpers;use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;trait ProxyTrait
{/** 创建Token* @param string $guard* @return mixed*/public function authenticate($guard = ''){$client = new Client();try {$url = request()->root() . '/oauth/token';$params = ['grant_type' => env('OAUTH_GRANT_TYPE'),'client_id' => env('OAUTH_CLIENT_ID'),'client_secret' => env('OAUTH_CLIENT_SECRET'),'username' => request('mobile'),'password' => request('password'),'scope' => env('OAUTH_SCOPE')];if ($guard) {$params = array_merge($params, ['provider' => $guard,]);}$respond = $client->request('POST', $url, ['form_params' => $params]);} catch (RequestException $exception) {return false;}if ($respond->getStatusCode() === 200) {return json_decode($respond->getBody()->getContents(), true);}return false;}/** 刷新token* @return mixed*/public function getRefreshtoken(){$client = new Client();try {$url = request()->root() . '/oauth/token';$params = array_merge(config('passport.refresh_token'), ['refresh_token' => request('refresh_token'),]);$respond = $client->request('POST', $url, ['form_params' => $params]);} catch (RequestException $exception) {return false;}if ($respond->getStatusCode() === 200) {return json_decode($respond->getBody(), true);}return false;}
}

创建路由(router/api.php) 验证为:auth中间件,guards为api

Route::post('login', 'API\UserController@login');
Route::post('register', 'API\UserController@register');Route::group(['middleware' => 'auth:api'], function(){Route::post('details', 'API\UserController@details');
});

编辑控制器

  1. 利用passport自带方法,实现token请求
  2. 利用封装工具来实现获取token
    注意 获取下一个token,记得删除上一个token值(如果不删除之前的token也可以验证成功)
<?phpnamespace App\Http\Controllers\API;use App\Helpers\ProxyTrait;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Support\Facades\Auth;
use Validator;class UserController extends Controller
{use ProxyTrait;public $successStatus = 200;/*** 登录* */public function login(){if(Auth::attempt(['email' => request('email'), 'password' => request('password')])){$user = Auth::user();//删除之前的token(此删除适合方法一)DB::table('oauth_access_tokens')->where('user_id',$user->id)->where('name','MyApp')->update(['revoked'=>1]);//方法一:获取新的token$success['token'] =  $user->createToken('MyApp')->accessToken;//方法二:获取新的token(先引入ProxyTrait工具)$token = $this->authenticate();$user['token'] = $token['access_token'];return response()->json(['success' => $success], $this->successStatus);}else{return response()->json(['error'=>'Unauthorised'], 401);}}/*** 注册*/public function register(Request $request){$validator = Validator::make($request->all(), ['name' => 'required','email' => 'required|email','password' => 'required','c_password' => 'required|same:password',]);if ($validator->fails()) {return response()->json(['error'=>$validator->errors()], 401);            }$input = $request->all();$input['password'] = bcrypt($input['password']);$user = User::create($input);//方法一:获取token(注册成功后自动登录)$success['token'] =  $user->createToken('MyApp')->accessToken;$success['name'] =  $user->name;return response()->json(['success'=>$success], $this->successStatus);}/*** 获取用户详情*/public function details(){$user = Auth::user();return response()->json(['success' => $user], $this->successStatus);}
}

3. 多表用户登录

现在大部分公司用到前后端分离技术,可能存在APP,Web,小程序共存的情况,就会出现多表处理Token值;已微信小程序登录为例:

3.1 修改项目auth配置文件(UserCus表为用户表)

文件位置:congig/auth.php 接口使用xcx, 保护项( driver )改为 passport 。此调整会让你的应用程序在接收到 API 的授权请求时使用 Passport 的 TokenGuard 来处理:


<?php
return ['defaults' => ['guard' => 'web','passwords' => 'users',],'guards' => ['web' => ['driver' => 'session','provider' => 'users',],'api' => ['driver' => 'passport','provider' => 'users',],'xcx' => ['driver' => 'passport','provider' => 'usercu',]],'providers' => ['users' => ['driver' => 'eloquent','model' => App\User::class,],'usercu' => ['driver' => 'database','model' => 'App\Models\UserCu::class',],],'passwords' => ['users' => ['provider' => 'users','table' => 'password_resets','expire' => 60,],'usercu' => ['provider' => 'usercu','table' => 'password_resets','expire' => 60,],],
];

3.2 创建用户模型

1.根据自己需求设置username , 系统默认为email
2.根据自己需求设置password验证,系统默认为Hash算法
3.新建表如果报错没有创建关联关系,请创建

<?phpnamespace App\Models;use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;class Usercu extends Model
{use HasApiTokens,Notifiable;/** 修改默认验证用户名* @param $username* @return mixed*/public function findForPassport($username){return $this->Where('phone', $username)->first();}/** 修改验证密码规则* @param $password* @return bool*/public function validateForPassportPasswordGrant($password){//如果请求密码等于数据库密码 返回true(此为实例,根据自己需求更改)if($password == $this->password){return true;}return false;}/** Auth2.0 设置用户ID(创建关联关系,如果你本地没有报错,就不需要使用这一句)*  使用位置:Laravel\Passport\Bridge\UserRepository* @return mixed*/public function getAuthIdentifier(){return $this->id;}
}

3.3 创建中间件,设置config文件

由2.4.1源码我们可以看到$provider = config('auth.guards.api.provider');系统只默认了api这一种情况,可是我想验证xcx;代码如下:

<?phpnamespace App\Http\Middleware;use Closure;
use Illuminate\Support\Facades\Config;class PassportMiddleware
{/*** Handle an incoming request.** @param  \Illuminate\Http\Request  $request* @param  \Closure  $next* @return mixed*/public function handle($request, Closure $next){$params = $request->all();if (array_key_exists('provider', $params)) {Config::set('auth.guards.api.provider', $params['provider']);}return $next($request);}
}
?>

3.4 给获取token路由加上中间件

更改步骤2.5文件中的代码,加一个中间件,根据实际需求去加;代码如下:

<?phpnamespace App\Providers;use Carbon\Carbon;
use Laravel\Passport\RouteRegistrar;
use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;class AuthServiceProvider extends ServiceProvider
{/*** The policy mappings for the application.** @var array*/protected $policies = ['App\Model' => 'App\Policies\ModelPolicy',];/*** Register any authentication / authorization services.** @return void*/public function boot(){$this->registerPolicies();//        Passport::routes(); //该方法将会注册发布/撤销访问令牌、客户端以及私人访问令牌所必需的路由Passport::routes(function (RouteRegistrar $router) {$router->forAccessTokens();}, ['middleware' => 'passport_validate']);//配置更短的令牌生命周期Passport::tokensExpireIn(Carbon::now()->addDays(15));Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));}
}

3.5 重置校验规则

根据自己需求重置,本人是因为前后端分离,多端多字段共同存在一个路由api中,所有要重置

<?php
namespace App\Http\Middleware;use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Auth\Middleware\Authenticate;
use Laravel\Passport\Http\Middleware\CheckClientCredentials;class Custom
{/** 重新校验Auth2.0校验规则* @param $request* @param Closure $next* @return mixed* @throws AuthenticationException*/public function handle($request, Closure $next) {// 1. new 校验(验证User表)$flags = 0;if ($flags == 0) {try {app(Authenticate::class)->handle($request, $next, 'api');$flags = 1;} catch (AuthenticationException $exception) {\Log::info(__METHOD__ . '|' . $exception->getMessage(), $exception->getTrace());}}// 2. old  校验(验证终端)if ($flags == 0) {try {app(CheckClientCredentials::class)->handle($request, $next);$flags = 1;} catch (AuthenticationException $exception) {\Log::info(__METHOD__ . '|' . $exception->getMessage(), $exception->getTrace());}}// 3. 小程序校验(验证UserCus表)if ($flags == 0) {try {app(Authenticate::class)->handle($request, $next, 'xcx');$flags = 1;} catch (AuthenticationException $exception) {\Log::info(__METHOD__ . '|' . $exception->getMessage(), $exception->getTrace());}}if ($flags == 0) {\Log::info(__METHOD__ . '|三种验证都失败了');throw new AuthenticationException();}return $next($request);}
}

注册中间件

文件位置App\Http\Kernel.php

protected $routeMiddleware = [···'Custom' => App\Http\Middleware\Custom,···];

3.6 配置完成,测试使用

创建路由(router/api.php) 验证为:中间件为3.5重置验证规则中间件

Route::post('login', 'API\UserController@login');
Route::post('register', 'API\UserController@register');
Route::post('wxlogin', 'API\UserController@wxlogin');Route::group(['middleware' => 'Custom'], function(){Route::post('details', 'API\UserController@details');
});

UserController新增wxlogin方法(下面方法为测试方法,根据自己实际需求写自己的方法)

<?phpnamespace App\Http\Controllers\API;use App\Helpers\ProxyTrait;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use APP\Models\Usercu;
use Illuminate\Support\Facades\Auth;class UserController extends Controller
{use ProxyTrait;/*** 登录* */public function wxlogin(){$user = Usercu::where('mobile' , request('phone'))->where('password', request('openid'))->first();if($user)//方法二:获取新的token(先引入ProxyTrait工具)$token = $this->authenticate('xcx');$user['token'] = $token['access_token'];return response()->json(['success' => $user], 200);}else{return response()->json(['error'=>'Unauthorised'], 401);}}···
}

好了,小伙伴们,最难之passport终于啃下,有没有什么欣喜若狂呢,本篇文章纯属自己总结,如果对你和身边的小伙伴有帮助,欢迎转载,如果有不对的地方,欢迎小伙的指出,谢谢您的观看


http://www.ppmy.cn/news/618763.html

相关文章

Nodejs利用passport验证用户登录

passport验证用户登录 最近学习用nodejsexpresssession实现用户登录&#xff0c;绕不开使用passport&#xff0c;系统记录一下学习和实践过程 1. Passport简介 passport.js是Nodejs中的一个做登录验证的中间件&#xff0c;极其灵活和模块化&#xff0c;并且可与Express、Sails…

jsd2205-csmall-passport(Day13)

1. 解析JWT时可能出现的错误 如果使用过期的JWT&#xff0c;在解析时将出现错误&#xff1a; io.jsonwebtoken.ExpiredJwtException: JWT expired at 2022-09-06T17:33:03Z. Current time: 2022-09-08T09:04:26Z, a difference of 142283930 milliseconds. Allowed clock sk…

Portal认证

Portal认证通常又称Web认证&#xff0c;用户上网时&#xff0c;必须在Portal认证页面进行认证&#xff0c;如果未认证成功&#xff0c;仅可以访问特定的网络资源&#xff0c;认证成功后&#xff0c;才可以访问其他网络资源。 Portal认证具有以下优点&#xff1a; 简单方便&…

Laravel Passport身份认证写法

在laravel中&#xff0c; 可以用Passport扩展来实现Oauth2.0的接口身份验证。 可以让用户在第三方客户端登录账号。 但是大多数时候只需要用户直接在我们的客户端上登录即可&#xff0c; 这时候需要适合用Passport里面的Personal Access Tokens方式来实现。 下面记录下安装和使…

Passport 授权码模式

交流群 375462817 授权码模式 哔哩哔哩提供一个“微信登陆”的链接&#xff0c;用户点击跳转到微信授权服务器。用户根据微信授权服务器提示登陆微信并确认授权给哔哩哔哩。微信授权服务器返回用户代理&#xff08;浏览器&#xff09;一个授权码。用户代理&#xff08;浏览器…

Microsoft Passport

Microsoft Passport统一的登录认证服务&#xff0c;MS维护用户的账号、密码&#xff0c;提供登录认证服务认证机制&#xff1a;1. 站点加入Passport Network时&#xff0c;得到公钥。站点与Passport Server之间的通讯可以使用数字签名、加密方式&#xff0c;数字签名和加密参考…

Travel Pass

题解模板 题目 题目链接 题意 分别有两种申请通行证的方式&#xff0c;一种申请需要a分钟&#xff0c;另一种需要b分钟&#xff0c;用一个“0”“1”字符串对其进行记录&#xff0c;从而计算申请花费的总时间 思路 1.字符串遍历2.记录“0”“1”出现的个数3.输出即为“0”出…

passport 实现

passport实现 随着社交网络的发展&#xff0c;开发一个应用门槛越来越低。从一个完整的应用系统&#xff0c;到一个部署在社交网络平台的APP&#xff1b;从数据库–》应用层–》展示层&#xff0c;变成只需要开发展示层。很多的社交应用&#xff0c;甚至都放弃了用户注册&…