双token验证登陆过期
JWT
不了解token或者JWT的可以先看这里JWT 基础概念详解 | JavaGuide,了解后可以直接跳过这一部分
简单来说,Token是用来鉴别用户身份的令牌,JWT是JSON Web Token一种规范化之后的 JSON 结构的 Token。
JWT 本质上就是一组字串,通过(.
)切分成三个为 Base64 编码的部分:
JWT通常长这样子:xxxxx.yyyyy.zzzzz
示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT在加密之前是json格式的,加密后才变得如此抽象,用.
分割的三部分在加密前都是JSON格式的数据,分别如下:
- Header : 描述 JWT 的元数据,定义了生成签名的算法以及
Token
的类型 - Payload : 用来存放实际需要传递的数据。其中我认为最主要的两个字段:sub字段存储想要保存的信息,exp字段保存该JWT的过期时间
- Signature(签名):服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。
而服务器端通过加密算法生成JWT,并将诸如用户ID,用户权限等信息放入Payload的sub字段,前端调用后端接口时把token作为参数(一般以http头的形式)传给后端,后端解析后得知调用这个参数的用户是哪一个,由此做出相应的响应。
为什么要使用双token
由于token的过期时间在token生成后就难以更改,所有token的过期时长不宜设置过长:
- 当用户信息发生更改时,token中的信息无法及时更改
- 增加token中信息泄露的风险
然而token过期时间短的话,用户需要不停登陆,体验较差
解决方案:使用双token验证登陆过期:
- 一个accessToken,过期时间较短,储存用户信息权限等
- 一个refreshToken,过期时间较长,不储存额外信息,只储存用户id
双token运作流程
- 前端请求登录/注册接口后后端会返回accessToken和refreshToken
- 请求需要登录的接口时,在请求头携带accessToken,key为“Authorization”,value为accessToken的值
- 后端首先验证accessToken是否过期,没过期就正常处理请求、返回结果;过期就返回errCode=409,errDesc="用户未登录"的结果
- 前端如果接收到了上述过期的结果,需要不携带任何请求头请求/api/user/refreshToken接口,传入参数refreshToken=“xxxx”。
- 如果refreshToken没过期,后端会返回新的accessToken和refreshToken,此时前端可以用拿到的新的accessToken重新请求登陆接口
- 如果refreshToken也过期了,/api/user/refreshToken接口同样会返回errCode=409,errDesc="用户未登录"的结果,此时前端需要提示用户长时间未操作,需要重新登陆(这次登陆不携带token,需要用户输入账号密码等方式登陆)
这个流程需要前端配合:
- 登陆接口收到409的响应后,需要请求/api/user/refreshToken接口
- /api/user/refreshToken接口返回新的accessToken后需要重新请求登陆接口
- /api/user/refreshToken接口返回409后需要跳转到登陆界面
后端双token代码:
JwtUtil.java:
JWT工具类,可以直接照搬使用
package com.neu.deliveryPlatform.util;
/**
* @Author laobuzhang
* @Description: jwt工具类,通过UUID算法生成JWT的id,通过subject参数将要储存的信息放在jwt的sub字段中
*/
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
/**
* JWT工具类
*/
public class JwtUtil {
//有效期为
public static final Long JWT_TTL = 60 * 60 * 24 * 1000L;// 一天
//设置秘钥明文
public static final String JWT_KEY = "BuXiWanGongZuoShi";
public static String getUUID() {
String token = UUID.randomUUID().toString().replaceAll(