一、新建一个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依赖配置
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<!-- JSON 解析器和生成器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
3.2、application.yml 增加配置
# 应用服务 WEB 访问端口
server:
port: 8080
spring:
# 应用名称
application:
name: jwt-demo
# redis配置
redis:
# 地址
host: 127.0.0.1
# 端口,默认为6379
port: 6379
# 连接超时时间
timeout: 10s
# 密码
password: wangjing
# JWT 配置
jwt:
# 加密密钥
secret: wangjing
# header 名称
header: Authorization
# token有效时长 S
expire:
accessToken: 3600
refreshToken: 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;
@Autowired
RedisUtil 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 {
@Resource
JwtTokenUtil jwtTokenUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 获取token
String 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 {
@Resource
private JwtFilter jwtFilter;
/**
* 不需要拦截地址
*/
public static final String[] EXCLUDE_URLS = {
"/login/**"
};
@Override
public 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 {
@Autowired
JwtTokenUtil jwtTokenUtil;
/**
* 登录
*
* @param loginBody
* @return
*/
@PostMapping("/login")
public Result<UserToken> login(@RequestBody LoginBody loginBody) {
// 业务验证:入参校验 + 用户信息校验查询
//TODO 涉及到业务,这里不在阐述
UserTokenInfo userTokenInfo = new UserTokenInfo();
userTokenInfo.setUserId(1L);
userTokenInfo.setUserName("wangjing");
userTokenInfo.setRealName("王京");
// 生成Token
UserToken userToken = jwtTokenUtil.createToekns(userTokenInfo);
return new Result<>(ResultTypeEnum.SUCCESS, userToken);
}
/**
* 刷新令牌
*
* @param refreshToken
* @return
*/
@PostMapping("/refreshToken/{refreshToken}")
public Result<UserToken> 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);
// 生成Token
UserToken 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 简单测试
注:以上内容仅提供参考和交流,请勿用于商业用途,如有侵权联系本人删除!