java实现api接口的token_API 开发中可选择传递 token 接口遇到的一个坑

在做 API 开发时,不可避免会涉及到登录验证,我使用的是jwt-auth

在登录中会经常遇到一个token过期的问题,在config/jwt.php默认设置中,这个过期时间是一个小时,不过为了安全也可以设置更小一点,我设置了为五分钟。

五分钟过期,如果就让用户去登录,这种体验会让用户直接抛弃你的网站,所以这就会使用到刷新token这个功能

正常情况下是写一个刷新token的接口,当过期的时候前端把过期的token带上请求这个接口换取新的token

不过为了方便前端也可以使用后端刷新返回,直至不可刷新,我用的就是这个方法:使用 Jwt-Auth 实现 API 用户认证以及无痛刷新访问令牌

而坑就是这样来的,

在必须需要登录验证的接口设置刷新token

namespace App\Http\Middleware;

use App\Services\StatusServe;

use Closure;

use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

use Tymon\JWTAuth\Exceptions\JWTException;

use Tymon\JWTAuth\Exceptions\TokenExpiredException;

use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;

class CheckUserLoginAndRefreshToken extends BaseMiddleware

{

/**

* 检查用户登录,用户正常登录,如果 token 过期

* 刷新 token 从响应头返回

*

* @param $request

* @param Closure $next

* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response

* @throws JWTException

*/

public function handle($request, Closure $next)

{

/****************************************

* 检查token 是否存在

****************************************/

$this->checkForToken($request);

try {

/****************************************

* 尝试通过 tokne 登录,如果正常,就获取到用户

* 无法正确的登录,抛出 token 异常

****************************************/

if ($this->auth->parseToken()->authenticate()) {

return $next($request);

}

throw new UnauthorizedHttpException('jwt-auth', 'User not found');

} catch (TokenExpiredException $e) {

try {

/****************************************

* token 过期的异常,尝试刷新 token

* 使用 id 一次性登录以保证此次请求的成功

****************************************/

$token = $this->auth->refresh();

$id = $this->auth

->manager()

->getPayloadFactory()

->buildClaimsCollection()

->toPlainArray()['sub'];

auth()->onceUsingId($id);

} catch (JWTException $e) {

/****************************************

* 如果捕获到此异常,即代表 refresh 也过期了,

* 用户无法刷新令牌,需要重新登录。

****************************************/

throw new UnauthorizedHttpException('jwt-auth', $e->getMessage(), null, StatusServe::HTTP_PAYMENT_REQUIRED);

}

}

// 在响应头中返回新的 token

return $this->setAuthenticationHeader($next($request), $token);

}

}

而有些页面,比如文章列表页面,这个接口登录与不登录皆可访问,不过登录的时候可以在页面上显示是否点赞了这篇文章。所以这个接口直接使用的是jwt-auth默认的option中间件

/*

* This file is part of jwt-auth.

*

* (c) Sean Tymon

*

* For the full copyright and license information, please view the LICENSE

* file that was distributed with this source code.

*/

namespace Tymon\JWTAuth\Http\Middleware;

use Closure;

use Exception;

class Check extends BaseMiddleware

{

/**

* Handle an incoming request.

*

* @param \Illuminate\Http\Request $request

* @param \Closure $next

*

* @return mixed

*/

public function handle($request, Closure $next)

{

if ($this->auth->parser()->setRequest($request)->hasToken()) {

try {

$this->auth->parseToken()->authenticate();

} catch (Exception $e) {

}

}

return $next($request);

}

}

一开始也没有发现问题,直到测试的时候,发现文章列表页面点赞过的文章,过了一段时间再刷新的时候发现不显示已点赞,但是进入个人中心的已点赞文章可以看到。

刚开始测试没找出原因,直接暴力调试代码,发现没获取到登录用户,一想不对呀,已经传token为何获取不到。经过发现,去到个人中心,再回到新闻列表页就可以正常显示,过了一段时间又不显示了。

经过这一轮之后,大概明白,在新闻列表页时,token已经过期,但是当时图方便用的jwt-auth默认的中间件,不会刷新token,所以这个接口获取不到登录的用户。当进入个人中心,发现当前token已经过期,后台刷新token返回,这时候再回到文章列表页就可以得到正常的数据,一段时间后,token又失效了,所以有无法看到点赞过的文章

解决方法,自己写一个option中间件,当存在token的时候,也需要做token刷新处理。

namespace App\Http\Middleware;

use Closure;

use Exception;

class Check extends BaseMiddleware

{

public function handle($request, Closure $next)

{

if ($this->auth->parser()->setRequest($request)->hasToken()) {

try {

$this->auth->parseToken()->authenticate();

} catch (TokenExpiredException $e) {

// 此处做刷新 token 处理

// 具体代码可以参考必须需要登录验证的接口

// 在响应头中返回新的 token

return $this->setAuthenticationHeader($next($request), $token);

} catch (Exception $e) {

}

}

return $next($request);

}

}

问题解决。

最后说一个并发会出现的问题:

# 当前 token_1 过期,先发起 a 请求,之后马上发起 b 请求

# a 请求到服务器,服务器判断过期,刷新 token_1

# 之后返回 token_2 给 a 请求响应

# 这时候迟一点的 b 请求用的还是 token_1

# 服务器已经将此 token_1 加入黑名单,所以 b 请求无效

token_1 刷新返回 token_2

a 请求 --------> server -------> 成功

token_1 过期的 token_1,应该使用 token_2

b 请求 --------> server ------> 失败

jwt-auth已经想到这种情况,我们只需要设置一个黑名单宽限时间即可

3f2d0869a1045eca74109878825dc9f3.png

我设置为5秒,就是当token_1过期了,你还能继续使用token_1操作5秒时间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 实现 API 接口token 验证可以通过以下步骤进行: 1. 创建一个 TokenUtil 类,用于生成和验证 token。 ```java import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import javax.crypto.SecretKey; import java.util.Date; public class TokenUtil { private static final String SECRET_KEY = "your_secret_key_here"; // 生成 token public static String generateToken(String userId) { SecretKey key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes()); return Jwts.builder() .setSubject(userId) .setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 设置过期时间为一天 .signWith(key) .compact(); } // 验证 token public static boolean validateToken(String token) { try { SecretKey key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes()); Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); return true; } catch (Exception e) { return false; } } // 获取 token 的用户ID public static String getUserIdFromToken(String token) { SecretKey key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes()); Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); return claims.getSubject(); } } ``` 2. 在需要验证 tokenAPI 接口,使用 TokenUtil 类进行验证。 ```java import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; @Path("/api") public class MyApi { @GET @Path("/someEndpoint") public Response someEndpoint(@QueryParam("token") String token) { if (TokenUtil.validateToken(token)) { String userId = TokenUtil.getUserIdFromToken(token); // 验证通过,执行相应的逻辑 return Response.ok().build(); } else { // 验证失败,返回错误信息 return Response.status(Response.Status.UNAUTHORIZED).build(); } } } ``` 在上述代码,`generateToken` 方法用于生成 token,`validateToken` 方法用于验证 token 的有效性,`getUserIdFromToken` 方法用于从 token 获取用户ID。在 API 接口,通过调用 `validateToken` 方法来验证传递token,如果验证通过,则可以根据需要执行相应的逻辑。 请注意,上述代码的 SECRET_KEY 是用于签名和验证 token 的密钥,应该保持安全,并且不应该直接暴露在代码。可以将其存储在配置文件或环境变量,并通过相应的方式获取。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值