使用的是thans/tp-jwt-auth 包。支持Header、Cookie、Param等多种传参方式。包含验证并且自动刷新等多种中间件。
官网:https://gitee.com/thans/jwt-auth
环境要求
- php >= 7.0
- thinkphp ^5.1.10 || ^6.0.0
我这里使用的是ThinkPHP6 + PHP7.3
安装
composer require thans/tp-jwt-auth
执行以下命令,将生成jwt.php,并且.env中会随机生成secret,请不要随意更新secret,也请保障secret安全。
php think jwt:create
使用方式:对于需要验证的路由或者模块添加中间件:
thans\jwt\middleware\JWTAuth::class
自定义认证中间件
说明:调用登录接口,成功则返回token给前端,所有需要用户认证的路由都需要在头部携带此token。(格式:将token加入header,如下:Authorization:bearer token值)
同时,后端会判断用户token是否过期,如果过期了会刷新token,并且在响应header返回新的token给前端,前端需要判断响应header有没token,如果有,则直接使用此 token 替换掉本地的 token,以此达到无痛刷新token效果。
创建用户认证中间件:
php think make:middleware JWT
代码:
- <?php
-
- declare (strict_types=1);
-
- namespace app\api\middleware;
-
- use thans\jwt\exception\JWTException;
- use thans\jwt\exception\TokenBlacklistException;
- use thans\jwt\exception\TokenBlacklistGracePeriodException;
- use thans\jwt\exception\TokenExpiredException;
- use thans\jwt\middleware\JWTAuth;
- use think\exception\HttpException;
-
- /**
- * JWT验证刷新token机制
- * Class JWTToken
- * @package app\api\middleware
- */
- class JWT extends JWTAuth
- {
- /**
- * 刷新token
- * @param $request
- * @param \Closure $next
- * @return mixed
- * @throws JWTException
- * @throws TokenBlacklistException
- * @throws TokenBlacklistGracePeriodException
- */
- public function handle($request, \Closure $next): object
- {
- try {
- $payload = $this->auth->auth();
- } catch (TokenExpiredException $e) { // 捕获token过期
- // 尝试刷新token,会将旧token加入黑名单
- try {
- $this->auth->setRefresh();
- $token = $this->auth->refresh();
- $payload = $this->auth->auth(false);
- } catch (TokenBlacklistGracePeriodException $e) {
- $payload = $this->auth->auth(false);
- } catch (JWTException $exception) {
- // 如果捕获到此异常,即代表 refresh 也过期了,用户无法刷新令牌,需要重新登录。
- throw new HttpException(401, $exception->getMessage());
- }
- } catch (TokenBlacklistGracePeriodException $e) { // 捕获黑名单宽限期
- $payload = $this->auth->auth(false);
- } catch (TokenBlacklistException $e) { // 捕获黑名单,退出登录或者已经自动刷新,当前token就会被拉黑
- throw new HttpException(401, '未登录..');
- }
-
- // 可以获取payload里自定义的字段,比如uid
- $request->uid = $payload['uid']->getValue();
-
- $response = $next($request);
-
- // 如果有新的token,则在响应头返回(前端判断一下响应中是否有 token,如果有就直接使用此 token 替换掉本地的 token,以此达到无痛刷新token效果)
- if (isset($token)) {
- $this->setAuthentication($response, $token);
- }
-
- return $response;
- }
- }
在路由中使用中间件
- Route::group(function () {
- Route::get('user', 'user/index');
- })->middleware(\app\api\middleware\JWT::class);
登录接口
- ......
- // 登录逻辑省略
- $user = xxxx;
- // 生成token,参数为用户认证的信息,请自行添加
- $token = JWTAuth::builder(['uid' => $user->id]);
- return [
- 'token' => 'Bearer ' . $token
- ];
- ......
使用Postman进行测试,注意参数名是:Authorization
前端自定义响应拦截器
- axios.interceptors.response.use((response) => {
- // 判断响应中是否有token,如果有则使用此token替换掉本地的token
- this.refreshToken(response);
- return response
- }, (error) => {
- // 判断错误响应中是否有token,如果有则使用此token替换掉本地的token
- this.refreshToken(error.response);
- switch (error.response.status) {
- // http状态码为401,则清除本地的数据并跳转到登录页面
- case 401:
- localStorage.removeItem('token');
- console.log('需要重新登录')
- break;
- // http状态码为400,则弹出错误信息
- case 400:
- console.log(error.response.data.error);
- break;
- }
- return Promise.reject(error)
- })
-
- .......
-
- methods: {
- // 刷新token
- refreshToken(response) {
- let token = response.headers.authorization
- if (token) {
- localStorage.setItem('token', token);
- }
- }
- }
前端需要用户认证请求示例
- axios.get('xxx', {
- headers: {
- Authorization: localStorage.getItem('token')
- }
- }).then(response => {
- console.log(response)
- }).catch(error => { //请求失败,error接收失败原因
- console.log(error);
- });
作者:皮蛋馅儿
链接:https://www.jianshu.com/p/b1e7606c6168
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。