客商认证以及无痛刷新访谈令牌,5中央银行使

最近在做一个公司的项目,前端使用 Vue.js,后端使用 Laravel 构建 Api 服务,用户认证的包本来是想用 Laravel Passport 的,但是感觉有点麻烦,于是使用了 jwt-auth 。

前言

安装#

Laravel5.5中利用Passport实现Auth认证的方法,laravel5.5auth

前言

最近在写一个前后端分离项目,本来想用 Jwt-auth Dingo 开发的,但是略感笨重,于是想到了 Laravel 的 Passport 和 5.5 新出的 Api Resource。Laravel Passport 是一套已经封装好的 OAuth2 服务端实现

OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。

OAuth 2.0 是目前比较流行的做法,它率先被Google, Yahoo, Microsoft, Facebook等使用。之所以标注为 2.0,是因为最初有一个1.0协议,但这个1.0协议被弄得太复杂,易用性差,所以没有得到普及。2.0是一个新的设计,协议简单清晰,但它并不兼容1.0,可以说与1.0没什么关系。

所以这里就不细说了,先来看看怎么安装它吧。

安装

安装 Passport

1.在你的 Shell 中执行以下命令

composer require laravel/passport

如果你使用的 Laravel 版本是 5.5 以下,你需要手动在 config/app.php 文件 providers 数组中加入如下代码

LaravelPassportPassportServiceProvider::class,

2.运行迁移文件

在你的 Shell 中执行如下命令

php artisan migrate

Passport 服务提供器使用框架注册自己的迁移目录,因此在注册服务后,你可以直接运行 php artisan migrate 来为 Passport 生成所需的数据表

3.生成加密密钥

在你的 Shell 中执行如下命令

php artisan passport:install

此命令会创建生成安全访问令牌时所需的加密密钥,同时,这条命令也会创建用于生成访问令牌的「个人访问」客户端和「密码授权」。

4.添加 Trait

将 LaravelPassportHasApiTokens Trait 添加到 AppUser 模型中

<?php
namespace App;
use LaravelPassportHasApiTokens;
use IlluminateNotificationsNotifiable;
use IlluminateFoundationAuthUser as Authenticatable;
class User extends Authenticatable
{
 use HasApiTokens, Notifiable;
}

5.注册路由

在 AuthServiceProvider 的 boot 方法中调用 Passport::routes 函数。

class AuthServiceProvider extends ServiceProvider
{
 public function boot()
 {
  $this->registerPolicies();
  Passport::routes();
 }
}

如果你的程序是需要前后端分离形式的OAuth认证而不是多平台认证那么你可以在routers()方法中传递一个匿名函数来自定定义自己需要注册的路由,我这里是前后端分离的认证形式,因此我只需要对我的前端一个Client提供Auth的认证,所以我只注册了获取Token的路由,同时我还为它自定义了前缀名。

Passport::routes(function(RouteRegistrar $router) {
 $router->forAccessTokens();
},['prefix' => 'api/oauth']);

6.更改看守器驱动

将配置文件 config/auth.php 中授权看守器 guards 的 api 的 driver 选项改为 passport。此调整会让你的应用程序在在验证传入的 API 的请求时使用 Passport 的 TokenGuard 来处理

'guards' => [
 'web' => [
  'driver' => 'session',
  'provider' => 'users',
 ],
 'api' => [
  'driver' => 'passport',
  'provider' => 'users',
 ],
],

至此 Passport 已经安装完成,剩下的文档里所讲到的前端部分的话,由于我是只需要使用它做 Auth 的认证,并不需要实现完整的 OAuth 功能,所以我们完全可以不使用前端页面。

使用

为了 Api 返回数据方便,我封装了几个函数

function respond($status, $respond)
{
 return response()->json(['status' => $status, is_string($respond) ? 'message' : 'data' => $respond]);
}
function succeed($respond = 'Request success!')
{
 return respond(true, $respond);
}
function failed($respond = 'Request failed!')
{
 return respond(false, $respond);
}

respond 函数可以做基本返回,succeed 和 failed 是在 respond 函数上做的再次封装,用以返回请求成功和请求失败数据。

然后我们需要使用一层代理。

先说一下使用代理的原因,Passport 认证的流程是 从属应用带着 主应用

生成的 Client Token 和 用户输入的账号密码去请求主应用的 Passport Token 路由,以获得 access token (访问令牌) 和 refresh token (刷新令牌),然后带着得到的 access token 就可以访问 auth:api 下的路由了。但是我们并没有从属应用,是由前后端分离的前端来请求这个token,如果从前端想来拉取这个 access token 就需要把 Client token 写死在前端里,这样是很不合理的,所以我们可以在内部写一个代理,由应用自身带着 Client token 去请求自身以获取 access token,这样说可能有一点绕,大概请求过程是下面这个样子

1.前端带着用户输入的账号密码请求服务端

2.服务端带着从前端接收到账号与密码,并在其中添加 Client_id 与 Client_token,然后带着这些参数请求自身的 Passport 认证路由,然后返回认证后的 Access token 与 refresh token

下面是代码实现,我在 AppHttpControllersTraits 下新建了一个 ProxyHelpers 的 Trait,当然,这个函数是我根据我的业务逻辑自己封装的,如果不适合你的业务逻辑你可以自行调整。

<?php

namespace AppHttpControllersTraits;

use GuzzleHttpClient;
use AppExceptionsUnauthorizedException;
use GuzzleHttpExceptionRequestException;

trait ProxyHelpers
{
 public function authenticate()
 {
  $client = new Client();
  try {
   $url = request()->root() . '/api/oauth/token';
   $params = array_merge(config('passport.proxy'), [
    'username' => request('email'),
    'password' => request('password'),
   ]);
   $respond = $client->request('POST', $url, ['form_params' => $params]);
  } catch (RequestException $exception) {
   throw new UnauthorizedException('请求失败,服务器错误');
  }
  if ($respond->getStatusCode() !== 401) {
   return json_decode($respond->getBody()->getContents(), true);
  }
  throw new UnauthorizedException('账号或密码错误');
 }
}

config/passport.php 内容如下

<?php
return [
 'proxy' => [
  'grant_type' => env('OAUTH_GRANT_TYPE'),
  'client_id'  => env('OAUTH_CLIENT_ID'),
  'client_secret' => env('OAUTH_CLIENT_SECRET'),
  'scope'   => env('OAUTH_SCOPE', '*'),
 ],
];

env 文件内容如下

OAUTH_GRANT_TYPE=password
OAUTH_CLIENT_ID=2
OAUTH_CLIENT_SECRET=2HaTQJF33Sx98HjcKDiSVWZjrhVYGgkHGP8XLG1O
OAUTH_SCOPE=*

我们需要用到的 client token 是 id 为 2 的 client token,不要搞错了哟~

然后我们只需要在控制器中 use 这个 Trait,然后调用 $this->authenticate()就可以得到认证成功的 token,如果请求失败的话,你可以使用 catch 来捕捉错误抛出异常。

 public function login(Request $request)
{
  $needs = $this->validate($request, rules('login'));
  $user = User::where('email', $needs['email'])->first();

  if (!$user) {
   throw new UnauthorizedException('此用户不存在');
  }
  $tokens = $this->authenticate();
  return succeed(['token' => $tokens, 'user' => new UserResource($user)]);
}

得到的 tokens 返回如以下格式

{
 "token_type": "Bearer",
 "expires_in": 31536000,
 "access_token": "token_str",
 "refresh_token": "token_str"
}

做完这一切后你就可以在前端向这样子请求服务端了

axios.post('yourdomain/login',login_form).then(resource => { 
})

如果请求成功,那么你将会得到 用户的信息和 access token,refresh token。

然后在你的前端 http 请求 header 里需要加入一个参数 Authorization

axios.defaults.headers.common['Authorization'] = token.token_type   ' '   token.access_token

然后在你需要使用到 auth 认证的路由里使用中间件 auth:api,一切就大功告成啦~

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对帮客之家的支持。

前言 最近在写一个前后端分离项目,本来想用 Jwt-auth Dingo 开发的,但是略感笨重,...

jwt-auth 最新版本是 1.0.0 rc.1 版本,已经支持了 Laravel 5.5。如果你使用的是 Laravel 5.5 版本,可以使用如下命令安装。根据评论区 @tradzero 兄弟的建议,如果你是 Laravel 5.5 以下版本,也推荐使用最新版本,RC.1 前的版本都存在多用户token认证的安全问题。

最近在写一个前后端分离项目,本来想用 Jwt-auth Dingo 开发的,但是略感笨重,于是想到了 Laravel 的 Passport 和 5.5 新出的 Api Resource。Laravel Passport 是一套已经封装好的 OAuth2 服务端实现

安装 Passport#

$ composer require tymon/jwt-auth 1.0.0-rc.1

OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。

1.在你的 Shell 中执行以下命令

添加服务提供商

将下面这行添加至 config/app.php 文件 providers 数组中:

app.php

'providers' => [ ... TymonJWTAuthProvidersLaravelServiceProvider::class,]

OAuth 2.0 是目前比较流行的做法,它率先被Google, Yahoo, Microsoft, Facebook等使用。之所以标注为 2.0,是因为最初有一个1.0协议,但这个1.0协议被弄得太复杂,易用性差,所以没有得到普及。2.0是一个新的设计,协议简单清晰,但它并不兼容1.0,可以说与1.0没什么关系。

composerrequirelaravel/passport

发布配置文件

在你的 shell 中运行如下命令发布 jwt-auth 的配置文件:

shell

$ php artisan vendor:publish --provider="TymonJWTAuthProvidersLaravelServiceProvider"

此命令会在 config 目录下生成一个 jwt.php 配置文件,你可以在此进行自定义配置。

所以这里就不细说了,先来看看怎么安装它吧。

如果你使用的 Laravel 版本是 5.5 以下,你需要手动在config/app.php文件 providers 数组中加入如下代码

生成密钥

jwt-auth 已经预先定义好了一个 Artisan 命令方便你生成 Secret,你只需要在你的 shell 中运行如下命令即可:

shell

$ php artisan jwt:secret

此命令会在你的 .env 文件中新增一行 JWT_SECRET=secret

安装

LaravelPassportPassportServiceProvider::class,

配置 Auth guard

config/auth.php 文件中,你需要将 guards/driver 更新为 jwt

auth.php

'defaults' => [ 'guard' => 'api', 'passwords' => 'users',],...'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ],],

只有在使用 Laravel 5.2 及以上版本的情况下才能使用。

如果需要使用 jwt-auth 作为用户认证,我们需要对我们的 User 模型进行一点小小的改变,实现一个接口,变更后的 User 模型如下:

User.php

<?phpnamespace App;use TymonJWTAuthContractsJWTSubject;use IlluminateNotificationsNotifiable;use IlluminateFoundationAuthUser as Authenticatable;class User extends Authenticatable implements JWTSubject{ use Notifiable; // Rest omitted for brevity /** * Get the identifier that will be stored in the subject claim of the JWT. * * @return mixed */ public function getJWTIdentifier() { return $this->getKey(); } /** * Return a key value array, containing any custom claims to be added to the JWT. * * @return array */ public function getJWTCustomClaims() { return []; }}

jwt.php

<?phpreturn [ /* |-------------------------------------------------------------------------- | JWT Authentication Secret |-------------------------------------------------------------------------- | | 用于加密生成 token 的 secret | */ 'secret' => env('JWT_SECRET'), /* |-------------------------------------------------------------------------- | JWT Authentication Keys |-------------------------------------------------------------------------- | | 如果你在 .env 文件中定义了 JWT_SECRET 的随机字符串 | 那么 jwt 将会使用 对称算法 来生成 token | 如果你没有定有,那么jwt 将会使用如下配置的公钥和私钥来生成 token | */ 'keys' => [ /* |-------------------------------------------------------------------------- | Public Key |-------------------------------------------------------------------------- | | 公钥 | */ 'public' => env('JWT_PUBLIC_KEY'), /* |-------------------------------------------------------------------------- | Private Key |-------------------------------------------------------------------------- | | 私钥 | */ 'private' => env('JWT_PRIVATE_KEY'), /* |-------------------------------------------------------------------------- | Passphrase |-------------------------------------------------------------------------- | | 私钥的密码。 如果没有设置,可以为 null。 | */ 'passphrase' => env('JWT_PASSPHRASE'), ], /* |-------------------------------------------------------------------------- | JWT time to live |-------------------------------------------------------------------------- | | 指定 access_token 有效的时间长度,默认为1小时,您也可以将其设置为空,以产生永不过期的标记 | */ 'ttl' => env('JWT_TTL', 60), /* |-------------------------------------------------------------------------- | Refresh time to live |-------------------------------------------------------------------------- | | 指定 access_token 可刷新的时间长度。默认的时间为 2 周。 | 大概意思就是如果用户有一个 access_token,那么他可以带着他的 access_token | 过来领取新的 access_token,直到 2 周的时间后,他便无法继续刷新了,需要重新登录。 | */ 'refresh_ttl' => env('JWT_REFRESH_TTL', 20160), /* |-------------------------------------------------------------------------- | JWT hashing algorithm |-------------------------------------------------------------------------- | | 指定将用于对令牌进行签名的散列算法。 | */ 'algo' => env('JWT_ALGO', 'HS256'), /* |-------------------------------------------------------------------------- | Required Claims |-------------------------------------------------------------------------- | | 指定必须存在于任何令牌中的声明。 | | */ 'required_claims' => [ 'iss', 'iat', 'exp', 'nbf', 'sub', 'jti', ], /* |-------------------------------------------------------------------------- | Persistent Claims |-------------------------------------------------------------------------- | | 指定在刷新令牌时要保留的声明密钥。 | */ 'persistent_claims' => [ // 'foo', // 'bar', ], /* |-------------------------------------------------------------------------- | Blacklist Enabled |-------------------------------------------------------------------------- | | 为了使令牌无效,您必须启用黑名单。 | 如果您不想或不需要此功能,请将其设置为 false。 | */ 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), /* | ------------------------------------------------------------------------- | Blacklist Grace Period | ------------------------------------------------------------------------- | | 当多个并发请求使用相同的JWT进行时, | 由于 access_token 的刷新 ,其中一些可能会失败 | 以秒为单位设置请求时间以防止并发的请求失败。 | */ 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), /* |-------------------------------------------------------------------------- | Providers |-------------------------------------------------------------------------- | | 指定整个包中使用的各种提供程序。 | */ 'providers' => [ /* |-------------------------------------------------------------------------- | JWT Provider |-------------------------------------------------------------------------- | | 指定用于创建和解码令牌的提供程序。 | */ 'jwt' => TymonJWTAuthProvidersJWTNamshi::class, /* |-------------------------------------------------------------------------- | Authentication Provider |-------------------------------------------------------------------------- | | 指定用于对用户进行身份验证的提供程序。 | */ 'auth' => TymonJWTAuthProvidersAuthIlluminate::class, /* |-------------------------------------------------------------------------- | Storage Provider |-------------------------------------------------------------------------- | | 指定用于在黑名单中存储标记的提供程序。 | */ 'storage' => TymonJWTAuthProvidersStorageIlluminate::class, ],];

先来说明一下我想要达成的效果,我希望用户提供账号密码前来登录。如果登录成功,那么我会给前端颁发一个 access _token ,设置在 header 中以请求需要用户认证的路由。

同时我希望如果用户的令牌如果过期了,可以暂时通过此次请求,并在此次请求中刷新该用户的 access _token,最后在响应头中将新的 access _token 返回给前端,这样子可以无痛的刷新 access _token ,用户可以获得一个很良好的体验,所以开始动手写代码。

执行如下命令以新建一个中间件:

php artisan make:middleware RefreshToken

中间件代码如下:

RefreshToken.php

<?phpnamespace AppHttpMiddleware;use Auth;use Closure;use TymonJWTAuthExceptionsJWTException;use TymonJWTAuthHttpMiddlewareBaseMiddleware;use TymonJWTAuthExceptionsTokenExpiredException;use SymfonyComponentHttpKernelExceptionUnauthorizedHttpException;// 注意,我们要继承的是 jwt 的 BaseMiddlewareclass RefreshToken extends BaseMiddleware{ /** * Handle an incoming request. * * @param IlluminateHttpRequest $request * @param Closure $next * * @throws SymfonyComponentHttpKernelExceptionUnauthorizedHttpException * * @return mixed */ public function handle($request, Closure $next) { // 检查此次请求中是否带有 token,如果没有则抛出异常。 $this->checkForToken; // 使用 try 包裹,以捕捉 token 过期所抛出的 TokenExpiredException 异常 try { // 检测用户的登录状态,如果正常则通过 if ($this->auth->parseToken()->authenticate { return $next; } throw new UnauthorizedHttpException('jwt-auth', '未登录'); } catch (TokenExpiredException $exception) { // 此处捕获到了 token 过期所抛出的 TokenExpiredException 异常,我们在这里需要做的是刷新该用户的 token 并将它添加到响应头中 try { // 刷新用户的 token $token = $this->auth->refresh(); // 使用一次性登录以保证此次请求的成功 Auth::guard->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray; } catch (JWTException $exception) { // 如果捕获到此异常,即代表 refresh 也过期了,用户无法刷新令牌,需要重新登录。 throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage; } } // 在响应头中返回新的 token return $this->setAuthenticationHeader($next, $token); }}

我选用的 HTTP 请求套件是 axios。为了达到无痛刷新 token 的效果,我们需要对 axios 定义一个拦截器,用以接收我们刷新的 Token,代码如下:

app.js

import Vue from 'vue'import router from './router'import store from './store'import iView from 'iview'import 'iview/dist/styles/iview.css'Vue.usenew Vue({ el: '#app', router, store, created() { // 自定义的 axios 响应拦截器 this.$axios.interceptors.response.use( => { // 判断一下响应中是否有 token,如果有就直接使用此 token 替换掉本地的 token。你可以根据你的业务需求自己编写更新 token 的逻辑 var token = response.headers.authorization if  { // 如果 header 中存在 token,那么触发 refreshToken 方法,替换本地的 token this.$store.dispatch('refreshToken', token) } return response },  => { switch (error.response.status) { // 如果响应中的 http code 为 401,那么则此用户可能 token 失效了之类的,我会触发 logout 方法,清除本地的数据并将用户重定向至登录页面 case 401: return this.$store.dispatch break // 如果响应中的 http code 为 400,那么就弹出一条错误提示给用户 case 400: return this.$Message.error(error.response.data.error) break } return Promise.reject }})

Vuex 内的代码如下:

store/index.js

import Vue from 'vue'import Vuex from 'vuex'import axios from 'axios'Vue.useexport default new Vuex.Store({ state: { name: null, avatar: null, mobile: null, token: null, remark: null, auth: false, }, mutations: { // 用户登录成功,存储 token 并设置 header 头 logined(state, token) { state.auth = true state.token = token localStorage.token = token }, // 用户刷新 token 成功,使用新的 token 替换掉本地的token refreshToken(state, token) { state.token = token localStorage.token = token axios.defaults.headers.common['Authorization'] = state.token }, // 登录成功后拉取用户的信息存储到本地 profile(state, data) { state.name = data.name state.mobile = data.mobile state.avatar = data.avatar state.remark = data.remark }, // 用户登出,清除本地数据 logout{ state.name = null state.mobile = null state.avatar = null state.remark = null state.auth = false state.token = null localStorage.removeItem } }, actions: { // 登录成功后保存用户信息 logined({dispatch,commit}, token) { return new Promise(function (resolve, reject) { commit('logined', token) axios.defaults.headers.common['Authorization'] = token dispatch('profile').then => { resolve.catch => { reject }, // 登录成功后使用 token 拉取用户的信息 profile { return new Promise(function (resolve, reject) { axios.get('profile', {}).then(respond => { if (respond.status == 200) { commit('profile', respond.data) resolve() } else { reject }, // 用户登出,清除本地数据并重定向至登录页面 logout { return new Promise(function (resolve, reject) { commit axios.post('auth/logout', {}).then(respond => { Vue.$router.push({name:'login'}) }) }) }, // 将刷新的 token 保存至本地 refreshToken({commit},token) { return new Promise(function (resolve, reject) { commit('refreshToken', token) }) }, }})

由于我们构建的是 api 服务,所以我们需要更新一下 app/Exceptions/Handler.php 中的 render

方法,自定义处理一些异常。

Handler.php

<?phpnamespace AppExceptions;use Exception;use IlluminateFoundationExceptionsHandler as ExceptionHandler;use IlluminateValidationValidationException;use SymfonyComponentHttpKernelExceptionUnauthorizedHttpException;class Handler extends ExceptionHandler{ ... /** * Render an exception into an HTTP response. * * @param IlluminateHttpRequest $request * @param Exception $exception * @return IlluminateHttpResponse */ public function render($request, Exception $exception) { // 参数验证错误的异常,我们需要返回 400 的 http code 和一句错误信息 if ($exception instanceof ValidationException) { return response(['error' => array_first(array_collapse($exception->errors], 400); } // 用户认证的异常,我们需要返回 401 的 http code 和错误信息 if ($exception instanceof UnauthorizedHttpException) { return response($exception->getMessage; } return parent::render($request, $exception); }}

更新完此方法后,我们上面自定义的中间件里抛出的异常和我们下面参数验证错误抛出的异常都会被转为指定的格式抛出。

现在,我们可以在我们的 routes/api.php 路由文件中新增几条路由来测试一下了:

api.php

Route::prefix->group(function { $router->post('login', 'AuthController@login'); $router->post('logout', 'AuthController@logout');});Route::middleware('refresh.token')->group(function { $router->get('profile','UserController@profile');});

在你的 shell 中运行如下命令以新增一个控制器:

$ php artisan make:controller AuthController

打开此控制器,写入如下内容

<?phpnamespace AppHttpControllers;use IlluminateHttpRequest;use IlluminateSupportFacadesAuth;use AppTransformersUserTransformer;class AuthController extends Controller{ /** * Get a JWT token via given credentials. * * @param IlluminateHttpRequest $request * * @return IlluminateHttpJsonResponse */ public function login(Request $request) { // 验证规则,由于业务需求,这里我更改了一下登录的用户名,使用手机号码登录 $rules = [ 'mobile' => [ 'required', 'exists:users', ], 'password' => 'required|string|min:6|max:20', ]; // 验证参数,如果验证失败,则会抛出 ValidationException 的异常 $params = $this->validate($request, $rules); // 使用 Auth 登录用户,如果登录成功,则返回 201 的 code 和 token,如果登录失败则返回 return ($token = Auth::guard->attempt ? response(['token' => 'bearer ' . $token], 201) : response(['error' => '账号或密码错误'], 400); } /** * 处理用户登出逻辑 * * @return IlluminateHttpJsonResponse */ public function logout() { Auth::guard->logout(); return response(['message' => '退出成功']); }}

然后我们进入 tinker

$ php artisan tinker

执行以下命令来创建一个测试用户,我这里的用户名是用的是手机号码,你可以自行替换为邮箱。别忘了设置命名空间哟:

>>> namespace AppModels;>>> User::create(['name' => 'Test','mobile' => 17623239881,'password' => bcrypt]);

正确执行结果如下图:

图片 11.png

然后打开 Postman 来进行 api 测试

图片 22.png

正确的请求结果如下图:

图片 33.png

可以看到我们已经成功的拿到了 token,接下来我们就去验证一下刷新 token 吧

图片 44.png

如图可以看到我们已经拿到了新的 token,接下来的事情便会交由我们前面设置的 axios 拦截器处理,它会将本地的 token 替换为此 token。

感觉蛮多人对版本没什么概念,所以在这里科普下常见的版本。

  • ### α版

    ​ 这个版本表示该 Package 仅仅是一个初步完成品,通常只在开发者内部交流,也有很少一部分发布给专业测试人员。一般而言,该版本软件的 Bug 较多,普通用户最好不要安装。

  • ### β版

    该版本相对于 α版已有了很大的改进,修复了严重的错误,但还是存在着一些缺陷,需要经过大规模的发布测试来进一步消除。通过一些专业爱好者的测试,将结果反馈给开发者,开发者们再进行有针对性的修改。该版本也不适合一般用户安装。

  • ### RC/ Preview版

    RC 即 Release Candidate 的缩写,作为一个固定术语,意味着最终版本准备就绪。一般来说 RC 版本已经完成全部功能并清除大部分的 BUG。一般到了这个阶段 Package 的作者只会修复 Bug,不会对软件做任何大的更改。

  • ### 普通发行版本

    一般在经历了上面三个版本后,作者会推出此版本。此版本修复了绝大部分的 Bug,并且会维护一定的时间。(时间根据作者的意愿而决定,例如 Laravel 的一般发行版本会提供为期一年的维护支持。)

  • ### LTS(Long Term Support) 版

    该版本是一个特殊的版本,和普通版本旨在支持比正常时间更长的时间。(例如 Laravel 的 LTS 版本会提供为期三年的 维护支持。)

jwt-auth 确实是一个很棒的用户认证 Package,配置简单,使用方便。

原文:

安装 Passport

2.运行迁移文件

1.在你的 Shell 中执行以下命令

在你的 Shell 中执行如下命令

composer require laravel/passport

php artisan migrate

如果你使用的 Laravel 版本是 5.5 以下,你需要手动在 config/app.php 文件 providers 数组中加入如下代码

Passport 服务提供器使用框架注册自己的迁移目录,因此在注册服务后,你可以直接运行php artisan migrate来为 Passport 生成所需的数据表

LaravelPassportPassportServiceProvider::class,

3.生成加密密钥

2.运行迁移文件

在你的 Shell 中执行如下命令

在你的 Shell 中执行如下命令

php artisan passport:install

php artisan migrate

此命令会创建生成安全访问令牌时所需的加密密钥,同时,这条命令也会创建用于生成访问令牌的「个人访问」客户端和「密码授权」。

Passport 服务提供器使用框架注册自己的迁移目录,因此在注册服务后,你可以直接运行 php artisan migrate 来为 Passport 生成所需的数据表

4.添加 Trait

3.生成加密密钥

将 LaravelPassportHasApiTokens Trait 添加到 AppUser 模型中

在你的 Shell 中执行如下命令

5.注册路由

php artisan passport:install

在 AuthServiceProvider 的 boot 方法中调用 Passport::routes 函数。

此命令会创建生成安全访问令牌时所需的加密密钥,同时,这条命令也会创建用于生成访问令牌的「个人访问」客户端和「密码授权」。

classAuthServiceProviderextendsServiceProvider{publicfunctionboot(){$this->registerPolicies();Passport::routes();}}

4.添加 Trait

如果你的程序是需要前后端分离形式的OAuth认证而不是多平台认证那么你可以在routers()方法中传递一个匿名函数来自定定义自己需要注册的路由,我这里是前后端分离的认证形式,因此我只需要对我的前端一个Client提供Auth的认证,所以我只注册了获取Token的路由,同时我还为它自定义了前缀名。

将 LaravelPassportHasApiTokens Trait 添加到 AppUser 模型中

Passport::routes(function(RouteRegistrar$router){$router->forAccessTokens();},['prefix'=>'api/oauth']);

<?php
namespace App;
use LaravelPassportHasApiTokens;
use IlluminateNotificationsNotifiable;
use IlluminateFoundationAuthUser as Authenticatable;
class User extends Authenticatable
{
 use HasApiTokens, Notifiable;
}

6.更改看守器驱动

5.注册路由

将配置文件 config/auth.php 中授权看守器 guards 的 api 的 driver 选项改为 passport。此调整会让你的应用程序在在验证传入的 API 的请求时使用 Passport 的 TokenGuard 来处理

在 AuthServiceProvider 的 boot 方法中调用 Passport::routes 函数。

'guards'=>['web'=>['driver'=>'session','provider'=>'users',],'api'=>['driver'=>'passport','provider'=>'users',],],

class AuthServiceProvider extends ServiceProvider
{
 public function boot()
 {
  $this->registerPolicies();
  Passport::routes();
 }
}

至此 Passport 已经安装完成,剩下的文档里所讲到的前端部分的话,由于我是只需要使用它做 Auth 的认证,并不需要实现完整的 OAuth 功能,所以我们完全可以不使用前端页面。

如果你的程序是需要前后端分离形式的OAuth认证而不是多平台认证那么你可以在routers()方法中传递一个匿名函数来自定定义自己需要注册的路由,我这里是前后端分离的认证形式,因此我只需要对我的前端一个Client提供Auth的认证,所以我只注册了获取Token的路由,同时我还为它自定义了前缀名。

使用#

Passport::routes(function(RouteRegistrar $router) {
 $router->forAccessTokens();
},['prefix' => 'api/oauth']);

为了 Api 返回数据方便,我封装了几个函数

6.更改看守器驱动

functionrespond($status,$respond){returnresponse()->json(['status'=>$status,is_string($respond)?'message':'data'=>$respond]);}functionsucceed($respond='Request success!'){returnrespond(true,$respond);}functionfailed($respond='Request failed!'){returnrespond(false,$respond);}

将配置文件 config/auth.php 中授权看守器 guards 的 api 的 driver 选项改为 passport。此调整会让你的应用程序在在验证传入的 API 的请求时使用 Passport 的 TokenGuard 来处理

respond 函数可以做基本返回,succeed 和 failed 是在 respond 函数上做的再次封装,用以返回请求成功和请求失败数据。

'guards' => [
 'web' => [
  'driver' => 'session',
  'provider' => 'users',
 ],
 'api' => [
  'driver' => 'passport',
  'provider' => 'users',
 ],
],

然后我们需要使用一层代理。

至此 Passport 已经安装完成,剩下的文档里所讲到的前端部分的话,由于我是只需要使用它做 Auth 的认证,并不需要实现完整的 OAuth 功能,所以我们完全可以不使用前端页面。

先说一下使用代理的原因,Passport 认证的流程是 从属应用带着 主应用

使用

生成的 Client Token 和 用户输入的账号密码去请求主应用的 Passport Token 路由,以获得 access token (访问令牌) 和 refresh token (刷新令牌),然后带着得到的 access token 就可以访问 auth:api 下的路由了。但是我们并没有从属应用,是由前后端分离的前端来请求这个token,如果从前端想来拉取这个 access token 就需要把 Client token 写死在前端里,这样是很不合理的,所以我们可以在内部写一个代理,由应用自身带着 Client token 去请求自身以获取 access token,这样说可能有一点绕,大概请求过程是下面这个样子

为了 Api 返回数据方便,我封装了几个函数

1.前端带着用户输入的账号密码请求服务端2.服务端带着从前端接收到账号与密码,并在其中添加 Client_id 与 Client_token,然后带着这些参数请求自身的 Passport 认证路由,然后返回认证后的 Access token 与 refresh token

function respond($status, $respond)
{
 return response()->json(['status' => $status, is_string($respond) ? 'message' : 'data' => $respond]);
}
function succeed($respond = 'Request success!')
{
 return respond(true, $respond);
}
function failed($respond = 'Request failed!')
{
 return respond(false, $respond);
}

下面是代码实现,我在 AppHttpControllersTraits 下新建了一个 ProxyHelpers 的 Trait,当然,这个函数是我根据我的业务逻辑自己封装的,如果不适合你的业务逻辑你可以自行调整。

respond 函数可以做基本返回,succeed 和 failed 是在 respond 函数上做的再次封装,用以返回请求成功和请求失败数据。

root().'/api/oauth/token';$params=array_merge(config('passport.proxy'),['username'=>request('email'),'password'=>request('password'),]);$respond=$client->request('POST',$url,['form_params'=>$params]);}catch(RequestException$exception){thrownewUnauthorizedException('请求失败,服务器错误');}if($respond->getStatusCode()!==401){returnjson_decode($respond->getBody()->getContents(),true);}thrownewUnauthorizedException('账号或密码错误');}}

然后我们需要使用一层代理。

config/passport.php内容如下

先说一下使用代理的原因,Passport 认证的流程是 从属应用带着 主应用

['grant_type'=>env('OAUTH_GRANT_TYPE'),'client_id'=>env('OAUTH_CLIENT_ID'),'client_secret'=>env('OAUTH_CLIENT_SECRET'),'scope'=>env('OAUTH_SCOPE','*'),],];

生成的 Client Token 和 用户输入的账号密码去请求主应用的 Passport Token 路由,以获得 access token (访问令牌) 和 refresh token (刷新令牌),然后带着得到的 access token 就可以访问 auth:api 下的路由了。但是我们并没有从属应用,是由前后端分离的前端来请求这个token,如果从前端想来拉取这个 access token 就需要把 Client token 写死在前端里,这样是很不合理的,所以我们可以在内部写一个代理,由应用自身带着 Client token 去请求自身以获取 access token,这样说可能有一点绕,大概请求过程是下面这个样子

env 文件内容如下

1.前端带着用户输入的账号密码请求服务端

OAUTH_GRANT_TYPE=passwordOAUTH_CLIENT_ID=2OAUTH_CLIENT_SECRET=2HaTQJF33Sx98HjcKDiSVWZjrhVYGgkHGP8XLG1OOAUTH_SCOPE=*

2.服务端带着从前端接收到账号与密码,并在其中添加 Client_id 与 Client_token,然后带着这些参数请求自身的 Passport 认证路由,然后返回认证后的 Access token 与 refresh token

我们需要用到的 client token 是 id 为 2 的 client token,不要搞错了哟~

下面是代码实现,我在 AppHttpControllersTraits 下新建了一个 ProxyHelpers 的 Trait,当然,这个函数是我根据我的业务逻辑自己封装的,如果不适合你的业务逻辑你可以自行调整。

然后我们只需要在控制器中 use 这个 Trait,然后调用$this->authenticate()就可以得到认证成功的 token,如果请求失败的话,你可以使用catch来捕捉错误抛出异常。

<?php

namespace AppHttpControllersTraits;

use GuzzleHttpClient;
use AppExceptionsUnauthorizedException;
use GuzzleHttpExceptionRequestException;

trait ProxyHelpers
{
 public function authenticate()
 {
  $client = new Client();
  try {
   $url = request()->root() . '/api/oauth/token';
   $params = array_merge(config('passport.proxy'), [
    'username' => request('email'),
    'password' => request('password'),
   ]);
   $respond = $client->request('POST', $url, ['form_params' => $params]);
  } catch (RequestException $exception) {
   throw new UnauthorizedException('请求失败,服务器错误');
  }
  if ($respond->getStatusCode() !== 401) {
   return json_decode($respond->getBody()->getContents(), true);
  }
  throw new UnauthorizedException('账号或密码错误');
 }
}

publicfunctionlogin(Request$request){$needs=$this->validate($request,rules('login'));$user=User::where('email',$needs['email'])->first();if(!$user){thrownewUnauthorizedException('此用户不存在');}$tokens=$this->authenticate();returnsucceed(['token'=>$tokens,'user'=>newUserResource($user)]);}

config/passport.php 内容如下

得到的 tokens 返回如以下格式

<?php
return [
 'proxy' => [
  'grant_type' => env('OAUTH_GRANT_TYPE'),
  'client_id'  => env('OAUTH_CLIENT_ID'),
  'client_secret' => env('OAUTH_CLIENT_SECRET'),
  'scope'   => env('OAUTH_SCOPE', '*'),
 ],
];

{"token_type":"Bearer","expires_in":31536000,"access_token":"token_str","refresh_token":"token_str"}

env 文件内容如下

做完这一切后你就可以在前端向这样子请求服务端了

OAUTH_GRANT_TYPE=password
OAUTH_CLIENT_ID=2
OAUTH_CLIENT_SECRET=2HaTQJF33Sx98HjcKDiSVWZjrhVYGgkHGP8XLG1O
OAUTH_SCOPE=*

axios.post('yourdomain/login',login_form).then(resource=>{})

我们需要用到的 client token 是 id 为 2 的 client token,不要搞错了哟~

如果请求成功,那么你将会得到 用户的信息和 access token,refresh token。

然后我们只需要在控制器中 use 这个 Trait,然后调用 $this->authenticate()就可以得到认证成功的 token,如果请求失败的话,你可以使用 catch 来捕捉错误抛出异常。

然后在你的前端 http 请求 header 里需要加入一个参数Authorization

 public function login(Request $request)
{
  $needs = $this->validate($request, rules('login'));
  $user = User::where('email', $needs['email'])->first();

  if (!$user) {
   throw new UnauthorizedException('此用户不存在');
  }
  $tokens = $this->authenticate();
  return succeed(['token' => $tokens, 'user' => new UserResource($user)]);
}

axios.defaults.headers.common['Authorization']=token.token_type ' ' token.access_token

得到的 tokens 返回如以下格式

然后在你需要使用到 auth 认证的路由里使用中间件auth:api,一切就大功告成啦~

{
 "token_type": "Bearer",
 "expires_in": 31536000,
 "access_token": "token_str",
 "refresh_token": "token_str"
}

转自Seaony

做完这一切后你就可以在前端向这样子请求服务端了

axios.post('yourdomain/login',login_form).then(resource => { 
})

如果请求成功,那么你将会得到 用户的信息和 access token,refresh token。

然后在你的前端 http 请求 header 里需要加入一个参数 Authorization

axios.defaults.headers.common['Authorization'] = token.token_type   ' '   token.access_token

然后在你需要使用到 auth 认证的路由里使用中间件 auth:api,一切就大功告成啦~

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

您可能感兴趣的文章:

  • Laravel 5框架学习之用户认证
  • Laravel实现用户多字段认证的解决方法
  • 通过修改Laravel Auth使用salt和password进行认证用户详解
  • Laravel多用户认证系统示例详解
  • 基于Laravel Auth自定义接口API用户认证的实现方法
  • Laravel认证原理以及完全自定义认证详解

本文由星彩网app下载发布于计算机编程,转载请注明出处:客商认证以及无痛刷新访谈令牌,5中央银行使

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。