Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 644|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-8-30 12:35:58 | 显示全部楼层 |阅读模式

     

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

    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
    • 1

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

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

     

    2.2 迁移数据库

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

    php artisan 

    2.3 生成加密键oauth_clients

    php artisan 

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

    2.4 修改user模型

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

    <?php namespace 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封装源码,然后根据自己需求更改我们的配置;追踪源码如下

    <?php namespace 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具体作用看注释

    <?php
    
    namespace 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' => '

    2.7 配置完成,测试使用

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

    <?php namespace 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也可以验证成功)
    <?php namespace 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.新建表如果报错没有创建关联关系,请创建

    <?php namespace 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;代码如下:

    <?php namespace 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文件中的代码,加一个中间件,根据实际需求去加;代码如下:

    <?php namespace 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方法(下面方法为测试方法,根据自己实际需求写自己的方法)

    <?php namespace 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); } } ··· }
    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-29 15:35 , Processed in 0.070077 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表