什么是JWTJWT是一个开放标准,它定义了一种用于简洁,包含的用于通信双方之间以JSON对象的形式安全传递信息的方法。可以使用HMAC算法或者是RSA的公钥密钥对进行签名简单来说:就是通过一定规范来生成token,然后可以通过解密算法逆向解密token,这样就可以获取用户信息 pom。xml!JWT相关dependencygroupIdio。jsonwebtokengroupIdjjwtartifactIdversion0。7。0versiondependency JWTUtil。javapackagecom。xmj。importcom。xmj。model。LoginUimportio。jsonwebtoken。Cimportio。jsonwebtoken。Jimportio。jsonwebtoken。SignatureAimportlombok。extern。slf4j。Slf4j;importjava。util。Dimportjava。util。HashMimportjava。util。MSlf4jpublicclassJWTUtil{token过期时间,正常是7天,方便测试我们设置70天privatestaticfinallongEXPIRE10006060710;加密的秘钥privatestaticfinalStringSECRETxmjcloud0417;令牌前缀privatestaticfinalStringTOKENPREFIXSUBJECTprivatestaticfinalStringSUBJECT根据用户信息生成tokenparamloginUserreturnpublicstaticMapgeneJsonWebToken(LoginUserloginUser){if(loginUsernull){thrownewNullPointerException(loginUser对象为空);}longdeadLineSystem。currentTimeMillis()EXPIRE;生成tokenStringtokenJwts。builder()。setSubject(SUBJECT)。claim(headImg,loginUser。getHeadImg())头像。claim(id,loginUser。getId())用户id。claim(email,loginUser。getMail())用户邮箱。claim(name,loginUser。getName())用户名称。setIssuedAt(newDate())生成日期。setExpiration(newDate(deadLine))过期时间为当前时间时间戳过期时间。signWith(SignatureAlgorithm。HS256,SECRET)。compact();签名算法和秘钥返回可以选择加前缀StringrealTokenTOKENPREFIXMapmapnewHashMap();map。put(accessToken,realToken);过期时间,refreshToken使用map。put(deadLine,deadLine);}校验token的方法paramtokenreturnpublicstaticClaimscheckJWT(Stringtoken){try{finalClaimsclaimsJwts。parser()。setSigningKey(SECRET)。parseClaimsJws(token。replace(TOKENPREFIX,))。getBody();}catch(Exceptione){log。info(解密失败);}}} 用户登录操作用户登录1、根据mail去找记录2、有的话,则使用秘钥和用户传递的明文密码,进行加密,在和数据库中的进行匹配paramloginRequestreturnOverridepublicJsonDatalogin(UserLoginRequestloginRequest){ListUserDOuserDOListuserMapper。selectList(newQueryWrapperUserDO()。eq(mail,loginRequest。getMail()));if(userDOList!nulluserDOList。size()1){已经注册UserDOuserDOuserDOList。get(0);StringcryptPwdMd5Crypt。md5Crypt(loginRequest。getPwd()。getBytes(),userDO。getSecret());if(cryptPwd。equals(userDO。getPwd())){登陆成功,生成accessToken、accessToken过期时间以及refreshTokenLoginUseruserDTOnewLoginUser();BeanUtils。copyProperties(userDO,userDTO);MapmapJWTUtil。geneJsonWebToken(userDTO);生成refreshToken,页面静默获取token时候使用StringrefreshTokenCommonUtil。generateUUID();map。put(refreshToken,refreshToken);redisTemplate。opsForValue()。set(refreshToken,随意的value值,100060602430);returnJsonData。buildSuccess(map);}else{returnJsonData。buildResult(BizCodeEnum。ACCOUNTPWDERROR);}}else{未注册returnJsonData。buildResult(BizCodeEnum。ACCOUNTPWDERROR);}} 在前后分离场景下,越来越多的项目使用jwttoken作为接口的安全机制,但存在jwt过期后,用户无法直接感知,假如在用户操作页面期间,突然提示登录,则体验很不友好,所以就有了token自动刷新需求; 方案:前端控制检测token,无感知刷新 用户登录成功的时候,一次性给他两个Token,分别为AccessToken和RefreshToken AccessToken有效期较短,比如1天或者5天,用于正常请求 RefreshToken有效期可以设置长一些,例如10天、20天,作为刷新AccessToken的凭证 刷新方案:当AccessToken即将过期的时候,例如提前30分钟,客户端利用RefreshToken请求指定的API获取新的AccessToken并更新本地存储中的AccessToken 核心逻辑 1、登录成功后,jwt生成AccessTUUID生成RefreshToken并存储在服务端redis中,设置过期时间 2、接口返回3个字段AccessTokenRefreshToken访问令牌过期时间戳 3、由于RefreshToken存储在服务端redis中,假如这个RefreshToken也过期,则提示重新登录; 老王的疑问:RefreshToken有效期那么长,和直接将AccessToken的有效期延长有什么区别 答:RefreshToken不像AccessToken那样在大多数请求中都被使用,主要是本地检测accessToken快过期的时候才使用, 一般本地存储的时候,也不叫refreshToken,前端可以取个别名,混淆代码让攻击者不能直接识别这个就是刷新令牌 缺点:前端每次请求需要判断token距离过期时间 优点:后端压力小,代码逻辑改动不大 刷新token方法未实现。刷新tokenparamparamreturnApiOperation(刷新accessToken)PostMapping(valuelogin)publicJsonDatalogin(ApiParam(用户登录对象)RequestBodyUserLoginRequestloginRequest){找redis中refreshtoken是否存在refreshtoken存在,解密accessToken重新调用JWTUtil。geneJsonWebToken()生成accessToken重新生成refreshToken,并存储redis,设置过期时间返回给前端returnjsonD}