Springboot 整合 JWT + Redis 实现双Token 校验Demo(简单实现)
创始人
2024-02-10 07:02:17

一、新建一个SpringBoot 项目,springboot项目创建过程详见 

mac idea 创建 springboot 项目_JAVA·D·WangJing的博客-CSDN博客_mac idea创建springboot项目

二、SpringBoot 整合使用 Rdis

SpringBoot 项目 添加 redis配置_JAVA·D·WangJing的博客-CSDN博客_springboot添加redis

三、SpringBoot 整合 JWT

3.1、pom.xml依赖配置

io.jsonwebtokenjjwt0.7.0com.auth0java-jwt3.4.0com.alibabafastjson1.2.83

3.2、application.yml 增加配置

# 应用服务 WEB 访问端口
server:port: 8080spring:# 应用名称application:name: jwt-demo# redis配置redis:# 地址host: 127.0.0.1# 端口,默认为6379port: 6379# 连接超时时间timeout: 10s# 密码password: wangjing# JWT 配置
jwt:# 加密密钥secret: wangjing# header 名称header: Authorization# token有效时长 Sexpire:accessToken: 3600refreshToken: 4000

3.3、JwtToken 工具类

package com.wangjing.jwtdemo.util;import com.alibaba.fastjson.JSONObject;
import com.wangjing.jwtdemo.constants.Constants;
import com.wangjing.jwtdemo.vo.UserToken;
import com.wangjing.jwtdemo.vo.UserTokenInfo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.Date;/*** @author: wangjing* @createTime: 2022-11-23 14:55* @version: 1.0.0* @Description: JwtToken 工具类*/
@Component
public class JwtTokenUtil {@Value("${jwt.secret}")public String secret;@Value("${jwt.header}")public String header;@Value("${jwt.expire.accessToken}")public Integer accessTokenExpire;@Value("${jwt.expire.refreshToken}")public Integer refreshTokenExpire;@AutowiredRedisUtil redisUtil;/*** 创建 刷新令牌 与 访问令牌 关联关系** @param userToken* @param refreshTokenExpireDate*/public void tokenAssociation(UserToken userToken, Date refreshTokenExpireDate) {Long time = (refreshTokenExpireDate.getTime() - System.currentTimeMillis()) / 1000 + 100;redisUtil.set(userToken.getRefreshToken(), userToken.getAccessToken(), time);}/*** 根据 刷新令牌 获取 访问令牌** @param refreshToken*/public String getAccessTokenByRefresh(String refreshToken) {Object value = redisUtil.get(refreshToken);return value == null ? null : String.valueOf(value);}/*** 添加至黑名单** @param token* @param expireTime*/public void addBlacklist(String token, Date expireTime) {Long expireTimeLong = (expireTime.getTime() - System.currentTimeMillis()) / 1000 + 100;redisUtil.set(getBlacklistPrefix(token), "1", expireTimeLong);}/*** 校验是否存在黑名单** @param token* @return true 存在 false不存在*/public Boolean checkBlacklist(String token) {return redisUtil.hasKey(getBlacklistPrefix(token));}/*** 获取黑名单前缀** @param token* @return*/public String getBlacklistPrefix(String token) {return Constants.TOKEN_BLACKLIST_PREFIX + token;}/*** 获取 token 信息** @param userTokenInfo* @return*/public UserToken createToekns(UserTokenInfo userTokenInfo) {Date nowDate = new Date();Date accessTokenExpireDate = new Date(nowDate.getTime() + accessTokenExpire * 1000);Date refreshTokenExpireDate = new Date(nowDate.getTime() + refreshTokenExpire * 1000);UserToken userToken = new UserToken();BeanUtils.copyProperties(userTokenInfo, userToken);userToken.setAccessToken(createToken(userTokenInfo, nowDate, accessTokenExpireDate));userToken.setRefreshToken(createToken(userTokenInfo, nowDate, refreshTokenExpireDate));// 创建 刷新令牌 与 访问令牌 关联关系tokenAssociation(userToken, refreshTokenExpireDate);return userToken;}/*** 生成token** @param userTokenInfo* @return*/public String createToken(UserTokenInfo userTokenInfo, Date nowDate, Date expireDate) {return Jwts.builder().setHeaderParam("typ", "JWT").setSubject(JSONObject.toJSONString(userTokenInfo)).setIssuedAt(nowDate).setExpiration(expireDate).signWith(SignatureAlgorithm.HS512, secret).compact();}/*** 获取 token 中注册信息** @param token* @return*/public Claims getTokenClaim(String token) {try {return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();} catch (Exception e) {return null;}}/*** 验证 token 是否过期失效** @param token* @return true 过期 false 未过期*/public Boolean isTokenExpired(String token) {return getExpirationDate(token).before(new Date());}/*** 获取 token 失效时间** @param token* @return*/public Date getExpirationDate(String token) {return getTokenClaim(token).getExpiration();}/*** 获取 token 发布时间** @param token* @return*/public Date getIssuedAtDate(String token) {return getTokenClaim(token).getIssuedAt();}/*** 获取用户信息** @param token* @return*/public UserTokenInfo getUserInfoToken(String token) {String subject = getTokenClaim(token).getSubject();UserTokenInfo userTokenInfo = JSONObject.parseObject(subject, UserTokenInfo.class);return userTokenInfo;}/*** 获取用户名** @param token* @return*/public String getUserName(String token) {UserTokenInfo userInfoToken = getUserInfoToken(token);return userInfoToken.getUserName();}/*** 获取用户Id** @param token* @return*/public Long getUserId(String token) {UserTokenInfo userInfoToken = getUserInfoToken(token);return userInfoToken.getUserId();}}

3.4、JwtFilter 拦截器

package com.wangjing.jwtdemo.filter;import com.wangjing.jwtdemo.util.JwtTokenUtil;
import com.wangjing.jwtdemo.vo.UserTokenInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** @author: wangjing* @createTime: 2022-10-17 19:03* @version: 1.0.0* @Description: Jwt 拦截器*/
@Slf4j
@Component
public class JwtFilter extends HandlerInterceptorAdapter {@ResourceJwtTokenUtil jwtTokenUtil;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 获取tokenString token = request.getHeader(jwtTokenUtil.header);if (StringUtils.isEmpty(token)) {token = request.getParameter(jwtTokenUtil.header);}if (StringUtils.isEmpty(token)) {// 只是简单DEMO,这里直接返回false,可以自己进行添加log.error("token 不能为空!");return false;}// 判断token是否超时if (jwtTokenUtil.isTokenExpired(token)) {log.error("token 已失效!");return false;}// 判断 token 是否已在黑名单if (jwtTokenUtil.checkBlacklist(token)) {log.error("token 已被加入黑名单!");return false;}// 获取用户信息UserTokenInfo userInfoToken = jwtTokenUtil.getUserInfoToken(token);// 通过用户信息去判断用户状态,等业务//TODO 涉及到业务,这里不在阐述return true;}
}

3.5、WebConfig 类(注入拦截器)

package com.wangjing.jwtdemo.config;import com.wangjing.jwtdemo.filter.JwtFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;/*** @author: wangjing* @createTime: 2022-10-17 19:02* @version: 1.0.0* @Description: 请求拦截器*/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Resourceprivate JwtFilter jwtFilter;/*** 不需要拦截地址*/public static final String[] EXCLUDE_URLS = {"/login/**"};@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtFilter).addPathPatterns("/**").excludePathPatterns(EXCLUDE_URLS);}
}

3.6、LoginController(简单的代码实现)

package com.wangjing.jwtdemo.controller;import com.wangjing.jwtdemo.entity.Result;
import com.wangjing.jwtdemo.enums.ResultTypeEnum;
import com.wangjing.jwtdemo.util.JwtTokenUtil;
import com.wangjing.jwtdemo.vo.LoginBody;
import com.wangjing.jwtdemo.vo.UserToken;
import com.wangjing.jwtdemo.vo.UserTokenInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;/*** @author wangjing* @since 2022-10-18*/
@RestController
@RequestMapping("/login")
public class LoginController {@AutowiredJwtTokenUtil jwtTokenUtil;/*** 登录** @param loginBody* @return*/@PostMapping("/login")public Result login(@RequestBody LoginBody loginBody) {// 业务验证:入参校验 + 用户信息校验查询//TODO 涉及到业务,这里不在阐述UserTokenInfo userTokenInfo = new UserTokenInfo();userTokenInfo.setUserId(1L);userTokenInfo.setUserName("wangjing");userTokenInfo.setRealName("王京");// 生成TokenUserToken userToken = jwtTokenUtil.createToekns(userTokenInfo);return new Result<>(ResultTypeEnum.SUCCESS, userToken);}/*** 刷新令牌** @param refreshToken* @return*/@PostMapping("/refreshToken/{refreshToken}")public Result refreshToken(@PathVariable("refreshToken") String refreshToken) {// 判断token是否超时if (jwtTokenUtil.isTokenExpired(refreshToken)) {return new Result<>(ResultTypeEnum.TOKEN_INVALID);}// 刷新令牌 放入黑名单jwtTokenUtil.addBlacklist(refreshToken, jwtTokenUtil.getExpirationDate(refreshToken));// 访问令牌 放入黑名单String odlAccessToken = jwtTokenUtil.getAccessTokenByRefresh(refreshToken);if (!StringUtils.isEmpty(odlAccessToken)) {jwtTokenUtil.addBlacklist(odlAccessToken, jwtTokenUtil.getExpirationDate(odlAccessToken));}// 生成新的 访问令牌 和 刷新令牌UserTokenInfo userInfoToken = jwtTokenUtil.getUserInfoToken(refreshToken);// 生成TokenUserToken userToken = jwtTokenUtil.createToekns(userInfoToken);return new Result<>(ResultTypeEnum.TOKEN_INVALID, userToken);}/*** 登出** @return*/@PostMapping("/logOut/{token}")public Result logOut(@PathVariable("token") String token) {// 放入黑名单jwtTokenUtil.addBlacklist(token, jwtTokenUtil.getExpirationDate(token));return new Result<>(ResultTypeEnum.SUCCESS);}/*** 注销** @return*/@PostMapping("/logOff/{token}")public Result logOff(@PathVariable("token") String token) {// 修改用户状态//TODO 涉及到业务,这里不在阐述// 放入黑名单jwtTokenUtil.addBlacklist(token, jwtTokenUtil.getExpirationDate(token));return new Result<>(ResultTypeEnum.SUCCESS);}}

 3.7、本文只列出相对重要的类,具体Demo 下载地址:

https://download.csdn.net/download/wang_jing_jing/87127439

四、通过postman 简单测试

 

注:以上内容仅提供参考和交流,请勿用于商业用途,如有侵权联系本人删除!

相关内容

热门资讯

猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...