Laravel 自定义认证器实现开放平台 AccessToken 认证
本文目录
案例场景
比如我们在做开发平台的时候,总会有个 AccessToken
的获取,还有一个过期时间,那么我们在做这种的时候,默认下,我们的Laravel里面并没有这样的操作,你会说用 Passport OAuth
这个,说实在,这种在接口方面,并不是很有友好,那么我们怎么办,现在我可以自己自定义一个认证器。
需求整理
- 申请app_id
- 获得app_secret
- 请求数据去获取 access_token
微信公众平台就是这种模式,还有很多的其他开发平台,也是大致如此
微信公众平台的介绍
场景测试
1.创建一个账号先
2.获取AccessToken
3.检验结果
Laravel 自定义认证器
可以实现我们任何想要实现的登录形式
实现流程
- 创建认证器
- 编写认证器类
- 继承认证器基础类
在AuthServiceProvider
自定义一个
app/Providers/AuthServiceProvider.php
<?php
namespace App\Providers;
use App\Guards\OpenApiGuard;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
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();
Auth::extend('openApi', function ($app, $name, array $config) {
$guard = new OpenApiGuard($app['request']);
return $guard;
});
}
}
创建认证器 OpenApiGuard
app/Guards/OpenApiGuard.php
<?php
namespace App\Guards;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
class OpenApiGuard implements Guard
{
use GuardHelpers;
protected $app;
protected $request;
protected $inputKey;//表单值
public function __construct(Request $request)
{
$this->request = $request;
$this->inputKey = 'access_token';
}
/**
* Get the currently authenticated user.
*
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
}
/**
* Validate a user's credentials.
*
* @param array $credentials
* @return bool
*/
public function validate(array $credentials = [])
{
}
}
文档说了,我们的认证器需要返回一个实现 Illuminate\Contracts\Auth\Guard
的接口,因此我们需要继承下这个接口方法,GuardHelpers
帮我写好了几个判断的方法,我们引入下,这里我们看到有2个 方法,一个是user
,validate
,
那么我们只需要做好这个2个方法的检测就可以了。
创建所需数据库
这里以数据库作为存储,当然你也可以用redis
像刚才的场景,我们需要
创建好这3个表
php artisan make:model App\Models\Api -m
php artisan make:model App\Models\ApiToken -m
模型类文件
ApiToken
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ApiToken extends Model
{
protected $guarded = ['id'];
protected $dates = ['expired_at'];
/**
* 找回会员
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user(){
return $this->belongsTo(User::class,'user_id','id');
}
public function api()
{
return $this->belongsTo(Api::class, 'app_id', 'app_id');
}
}
Api
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Api extends Model
{
//主键
protected $primaryKey = 'app_id';
//主键类型
protected $keyType = 'string';
//自动增长false
public $incrementing = false;
//隐藏属性
protected $hidden = ['app_secret'];
//包含的是不允许批量赋值的数组,空表示全部可以
protected $guarded = [];
public function user()
{
return $this->belongsTo(User::class, 'user_id','id');
}
public function token()
{
return $this->hasOne(ApiToken::class, 'app_id', 'app_id');
}
}
User
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User 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',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function token()
{
return $this->hasOne(ApiToken::class, 'user_id','id');
}
public function api()
{
return $this->hasOne(Api::class, 'user_id','id');
}
}
认证器类
<?php
namespace App\Guards;
use App\Exceptions\OpenApiException;
use App\Models\ApiToken;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
class OpenApiGuard implements Guard
{
use GuardHelpers;
protected $app;
protected $request;
protected $inputKey;//表单值
public static $ttl=7200;//过期时间
public function __construct(Request $request)
{
$this->request = $request;
$this->inputKey = 'access_token';
}
/**
* Get the currently authenticated user.
*
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
if(!is_null($this->user)){
return $this->user;
}
$apiToken = $this->getApiToken();
return $this->user = $apiToken->user;
}
public function getApiToken(){
$token=$this->getRequestToken();
return $this->checkToken($token);
}
public function checkToken($token){
if (is_null($accessToken = ApiToken::where('access_token', $token)->first())) {
throw new OpenApiException('不存在此AccessToken');
}
if (Carbon::now()->lte($accessToken->expired_at)) {
return $accessToken;
}
throw new OpenApiException(
'AccessToken 过期'
);
}
public function getRequestToken(){
$token = $this->request->query($this->inputKey);
if (empty($token)) {
$token = $this->request->bearerToken();
}
if (! empty($token)) {
return $token;
}
throw new OpenApiException('缺少AccessToken值');
}
/**
* Validate a user's credentials.
*
* @param array $credentials
* @return bool
*/
public function validate(array $credentials = [])
{
if ($this->checkToken($credentials['access_token'])) {
return true;
}
return false;
}
}
异常处理类
<?php
namespace App\Exceptions;
use Throwable;
class OpenApiException extends \Exception
{
public function render($request)
{
return response()->json([
'code' => $this->getCode(),
'message' => $this->getMessage(),
]);
}
}
创建路由进行测试
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
//创建接口账号信息
Route::get('open/api/create', 'OpenApiController@create');
//获取AccessToken
Route::post('open/api/token', 'OpenApiController@token');
//获取认证账号信息
Route::post('open/api/user', 'OpenApiController@user')->middleware('auth:openApi');
控制器创建测试
<?php
namespace App\Http\Controllers;
use App\Guards\OpenApiGuard;
use App\Models\Api;
use App\Models\ApiToken;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class OpenApiController extends Controller
{
/**
* 创建账号
* @return \Illuminate\Http\JsonResponse
*/
public function create(){
//先创建个账号
$user_data=[
'name'=>'kongqi',
'email'=>'kongqi@qq.com',
'password'=>bcrypt('123456'),
];
$user=User::firstOrNew(['name'=>'kongqi'],$user_data);
if($user->app){
return response()->json($user->app->toArray());
}
$create_data=[
'user_id' => $user->id,
'app_id' => md5(uniqid('app_id', true) . $user->id),
'app_secret' => md5(Str::random(32)),
'mark' => '测试账号',
];
$create_data=Api::updateOrCreate(['user_id'=>$user->id],$create_data);
//还要把app_secret显示出来
return response()->json($create_data->makeVisible('app_secret')->toArray());
}
/**
* 取得token
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function token(Request $request){
$app_id=$request->input('app_id');
$app_secret=$request->input('app_secret');
$app=Api::where('app_id',$app_id)->where('app_secret',$app_secret)->first();
if(empty($app)){
return response()->json(['code'=>1,'msg'=>'找不到账号']);
}
$data= ApiToken::updateOrCreate([
'user_id' => $app->user_id,
'app_id' => $app->app_id,
], [
'access_token' => md5(uniqid('access_token', true) . $app->user_id . $app->app_id . $app->app_secret),
'expired_at' => Carbon::now()->addSeconds(OpenApiGuard::$ttl),
'user_id'=>$app->user_id,
'app_id'=>$app->app_id
]);
return response()->json($data->toArray());
}
/**
* 获取认证信息
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function user(Request $request){
$user=$request->user();//取得认证后的会员
$data['user_request']=$user;
$auth_user=Auth::guard('openApi')->user();
$data['auth_user']=$auth_user;
return response()->json($data);
}
}
过滤419令牌
app/Http/Middleware/VerifyCsrfToken.php
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
//
'open/api/*'
];
}
配置一个认证器
<?php
return [
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'openApi' => [
'driver' => 'openApi',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
'password_timeout' => 10800,
];
场景测试
1.创建一个账号先
2.获取AccessToken
3.检验结果
到此完成。
Laravel其他认证器使用
超级简易的
Auth::viaRequest('customToken', function ($request) {
return User::where('token', $request->token)->first();
});
认证器需要添加下
<?php
return [
...
'guards' => [
....
'customToken' => [
'driver' => 'customToken',
'provider' => 'users',
'hash' => false,
],
],
];
这种就不需要自己定义一个认证器类来实现,可以完成编写验证规则。最终你需要返回一个值,那么这个值就是作为认证器通过之后返回的值。
$auth_user=Auth::guard('customToken')->user();//就是这个值,上面返回什么,这里就取到什么
其他认证器不再讲解,看下官方的操作。
总结
以上你还可以将它发部成一个composer 包,这里不再深入打包成包,详细包打包,看这里 https://www.heibaiketang.com/blog/show/520.html
版权提示
1.除了标识原创之外,其他可能来源于网友的分享,仅供学习使用2.如您发现侵犯了您的权利,请联系我们删除
3.转载必须带本文链接,否则你将侵权
4.关于会员或其发布的相关内容均由会员自行提供,会员依法应对其提供的任何信息承担全部责任,本站不对此承担任何法律责任