oracle access manager token,Laravel 自带的 API 守卫驱动 token 使用详解

在Laravel框架中,默认的用户认证守卫有两个,web和api,web守卫默认的驱动是session,而api守卫默认的驱动是token。那么,该如何使用这个token驱动?

寻找 TokenGuard

通过 Auth::guard() 这个方法,可以追溯到 token 驱动对应的类。来看Illuminate\Auth\AuthManager中的代码:

/**

* Attempt to get the guard from the local cache.

*

* @param string $name

* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard

*/

public function guard($name = null)

{

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

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

}

/**

* Resolve the given guard.

*

* @param string $name

* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard

*

* @throws \InvalidArgumentException

*/

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.");

}

可以看到,默认情况下就会调用到 createTokenDriver 。来看看这个方法:

public function createTokenDriver($name, $config)

{

// The token guard implements a basic API token based guard implementation

// that takes an API token field from the request and matches it to the

// user in the database or another persistence layer where users are.

$guard = new TokenGuard(

$this->createUserProvider($config['provider'] ?? null),

$this->app['request']

);

$this->app->refresh('request', $guard, 'setRequest');

return $guard;

}

显然,api守卫默认的驱动就是TokenGuard。

解读 TokenGuard

/**

* Create a new authentication guard.

*

* @param \Illuminate\Contracts\Auth\UserProvider $provider

* @param \Illuminate\Http\Request $request

* @param string $inputKey

* @param string $storageKey

* @return void

*/

public function __construct(UserProvider $provider, Request $request, $inputKey = 'api_token', $storageKey = 'api_token')

{

$this->request = $request;

$this->provider = $provider;

$this->inputKey = $inputKey;

$this->storageKey = $storageKey;

}

/**

* Get the currently authenticated user.

*

* @return \Illuminate\Contracts\Auth\Authenticatable|null

*/

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();

if (! empty($token)) {

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

[$this->storageKey => $token]

);

}

return $this->user = $user;

}

从构造函数和 user() 方法中可以看出,默认使用

['api_token' => $token]

这个数组去获取用户,也就是说,在用户表中我们需要一个字段(默认api_token)去存储标识用户的 token。

开始使用 token 进行api认证

添加数据表字段

use Illuminate\Support\Facades\Schema;

use Illuminate\Database\Schema\Blueprint;

use Illuminate\Database\Migrations\Migration;

class AddUsersApiTokenField extends Migration

{

/**

* Run the migrations.

*

* @return void

*/

public function up()

{

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

$table->string('api_token', 60)->unique()->nullable()->after('password');

});

}

/**

* Reverse the migrations.

*

* @return void

*/

public function down()

{

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

$table->dropColumn('api_token');

});

}

}

创建登录控制器

这里不演示注册之类的,假设我们的 users 表中已经存在用户,先创建一个用于 api 登录的控制器。在每次登录的时候,更新一次用户的 api_token 。这里使用了 ThrottlesLogins ,用来控制登录的尝试次数。

namespace App\Http\Controllers\Api;

use Hash;

use App\User;

use Illuminate\Http\Request;

use App\Http\Controllers\Controller;

use Illuminate\Foundation\Auth\ThrottlesLogins;

use Illuminate\Validation\ValidationException;

class LoginController extends Controller

{

use ThrottlesLogins;

/**

* @param Request $request

* @return \Illuminate\Http\Response|void

* @throws ValidationException

*/

public function login(Request $request)

{

$this->validateLogin($request);

if ($this->hasTooManyLoginAttempts($request)) {

return $this->sendLockoutResponse($request);

}

return $this->attempLogin($request);

}

/**

* @param Request $request

*/

public function validateLogin(Request $request)

{

$this->validate($request, [

$this->username() => 'required|string',

'password' => 'required|string'

]);

}

/**

* @param Request $request

* @return \Illuminate\Http\Response|void

*/

protected function attempLogin(Request $request)

{

$this->incrementLoginAttempts($request);

$user = User::where('email', $request->email)->first();

if (!$user || !Hash::check($request->password, $user->password)) {

return $this->sendFailedLoginResponse($request);

}

// 更新 api_key

$api_token = uniqid($user->id);

$user->api_token = $api_token;

$user->save();

return $this->sendLoginResponse($request, $user);

}

/**

* @param Request $request

*/

protected function sendFailedLoginResponse(Request $request)

{

throw ValidationException::withMessages([

$this->username() => [trans('auth.failed')],

]);

}

/**

* @param Request $request

* @param User $user

* @return \Illuminate\Http\Response

*/

protected function sendLoginResponse(Request $request, User $user)

{

$this->clearLoginAttempts($request);

return \Response::make([

'user' => $user,

'token' => $user->api_token

]);

}

public function username()

{

return 'email';

}

}

添加登录路由

将 routes\api.php 修改如下:

Route::namespace('Api')->group(function () {

Route::post('login', 'LoginController@login');

}); # 登录路由

Route::middleware('auth:api')->get('/user', function (Request $request) {

return $request->user();

});

调试

测试之前先往 users 表中添加几个用户,以下是我的测试数据。

%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20180512221943.png

可以看到登录成功并且返回了 token 。

接下去我们使用获取到的 token 请求需要登录的接口,默认有一个,就是/user.

%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20180512222458.png

ok~ 已经成功返回了数据,说明登录成功了!

例子中,api_token 是通过 OAuth 2.0 Access Token 的形式传进去的,但这不是唯一的方法,可以查看 TokenGuard 中的 getTokenForRequest 这个方法,它告诉我们可以用四种不同的形式传入 api_token

总结

默认的 api token 认证虽然在文档中没有提及如何使用,但是通过查看代码,也很容易使用。但是,在我们不重写或者扩展 tokenGUard的情况下,api_token 简直就是裸奔,显然不可能就这样应用到项目中去。个人猜测,框架中提供这个功能是为了让我们更好的理解 api 认证的工作原理,方便我们开发自己需要的 guard ,而且官方文档也推荐我们使用 passport 或者 jwt 进行 api 认证。

本文出于个人对默认配置的好奇进而探究之后写下的一片文章,也借鉴了网上的部分文章(忘记查看的文章地址了,也懒得找了),如有理解不到位或者错误的,请!斧!正!

欢迎任何形式转载,记得注明出处哦!

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值