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

2018年08月19日 09:31:01 醉卧码场君莫笑 阅读数:1632
 

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);
}
} ···
}

Laravel Passport认证-多表、多字段解决方案的更多相关文章

  1. laravel 数据库操作(表、字段)

    1)创建表(make:migration create),例如创建 articles php artisan make:migration create_articles_table --create ...

  2. 【laravel5.6】 Laravel 数据迁移给表和字段添加注释

    1 引用DB use Illuminate\Support\Facades\DB; 2 up方法 public function up() { Schema::create('code_table', ...

  3. laravel Passport - 创建 REST API 用户认证以及Dingo/Api v2.0+Passport实现api认证

    第一部分: 安装passport 使⽤ Composer 依赖包管理器安装 Passport : composer require laravel/passport 接下来,将 Passport 的服 ...

  4. laravel Passport - Dingo/Api v2.0+Passport 实现 api 认证

    第一部分: 安装passport 使⽤ Composer 依赖包管理器安装 Passport : composer require laravel/passport 接下来,将 Passport 的服 ...

  5. Laravel Passport API 认证使用小结

    Laravel Passport API 认证使用小结 八月 4, 2017 发布在 Laravel 看到Laravel-China 社区常有人问 Laravel Passport 用于密码验证方式来 ...

  6. laravel passport加密jwt格式的access_token中的sub(user_id)字段

    在很多需求我们不希望别人知道用户在我们表中的 user_id :但是又想用数据库的自增 id 功能:一般时候在取出用户后加密 user_id 加密即可:但是总有那么几个不经意间就可能把我们的 user ...

  7. laravel 字段映射问题,表单中提交字段与数据表中字段不一致

    在遇到提交表单时,表单中的name属性与数据表中的字段不一致,报错, 解决方法: 参考1:提交表单的时候,表单的name属性和数据表字段名称是一样的,这样有什么不妥么? 你数据库的信息给前端透露得越多 ...

  8. 数据库的表的字段名称与实体类(pojo)不对应解决方案

    数据库的表的字段名称与实体类(pojo)不对应解决方案 数据库表 ![image-20200429130200825](C:%5CUsers%5C%E6%9E%97%E6%AD%A3%E6%98%8E ...

  9. MyBatis实体属性与表的字段不对应的解决方案

    1.通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一致,这样就可以表的字段名和实体类的属性名一一对应上,这种方式是通过在SQL语句中定义别名来解决字段名和属性名的映射关系的. ...

随机推荐

  1. 【转】 Android Studio --“Cannot resolve symbol” 解决办法

    Android Studio 无法识别同一个 package 里的其他类,将其显示为红色,但是 compile 没有问题.鼠标放上去后显示 “Cannot resolve symbol XXX”,重启 ...

  2. HDU2255 奔小康赚大钱 —— 二分图最大权匹配 KM算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255 奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    ...

  3. webstorm使用帮助(转自http://my.oschina.net/longteng2013/blog/138010),另外有部分内容摘自其它人博客

    为了更高效的开发代码,这里列出了一些webstorm的快捷键和zencoding 发表于1 年 前(2013-06-17 00:19)   阅读(2101) | 评论(2) 11人收藏此文章, 我要收 ...

  4. JAVASCRIPT 和 AJax 实现局部验证

    JSP页面 <td width="10%" class="main_matter_td">真实姓名</td> <td width= ...

  5. 小程序-demo:template

    ylbtech-小程序-demo: 1.返回顶部 1.app.js 2.app.json 3.app.wxss 4.project.config.json 5.pages 6.images 7. 2. ...

  6. openwrt 设置samba服务器与pc共享文件

    1,安装samba36-server和luci-app-samba opkg install samba36-server luci-app-samba Openwrt的samba配置是先读取/etc ...

  7. 二、Log4j基本使用方法

    转自:https://blog.csdn.net/luohai859/article/details/52250807 Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息 ...

  8. IDE工具的[多行光标编辑模式]

    Sublime Text3:Ctrl+Alt+上下键 Eclipse:Shift+Alt+A,进入退出多光标模式 IDEA:Shift + ctrl + alt + 鼠标左键 收集链接 IDEA:ht ...

  9. js字符串去除连续或全部重复字符

    js字符串去除连续重复字符 ()和\number 配合使用表示重复正则第number个括号内匹配到的内容,如:(\d)\1表示重复第一个匹配块(\d)即等价于如果(\d)匹配到a,则表达式为aa 相应 ...

  10. bzoj P1979 华容道【bfs+spfa】

    调死我了-- 首先观察移动方式,需要移动的格子每次移动到相邻格子,一定是先把空白格子挪过去,所以我们得到一种做法,就是bfs预处理出每一个格子的四联通格子之间的空白格子移动距离建边,注意这个移动是不能 ...