Java - SpringBoot整合JWT
前言
一. SpringBoot整合JWT
1.1 基础配置
1.pom
依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
<dependencies>
<!-- 配置类的一个支持注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- JWT相关 -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.1</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- 省略get/set等方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
2.application.yml
文件:
server:
port: 8080
config:
jwt:
secret: ASD!@#F^%A
expire: 600
1.2 Token配置类
Token
配置类JwtUtil
:
@Component
@Data
@ConfigurationProperties(prefix = "config.jwt")
public class JwtUtil {
private String secret;
private int expire;
public String buildToken(Long userId) {
Calendar expires = Calendar.getInstance();
expires.add(Calendar.SECOND, expire);
String token = JWT
.create()
.withSubject(String.valueOf(userId))
.withExpiresAt(expires.getTime())
// 第三部分Signature
.sign(Algorithm.HMAC256(secret));
return token;
}
public Long getSubject(String token) {
Long res = null;
if (StringUtils.isBlank(token)) {
return res;
}
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret)).build();
// 验证失败会抛出异常
DecodedJWT verify = verifier.verify(token);
String subject = verify.getSubject();
if (StringUtils.isNoneBlank(subject)) {
res = Long.parseLong(subject);
}
return res;
}
}
@ConfigurationProperties(prefix = "config.jwt")
:用于获取配置文件中的属性定义并绑定到Bean
的属性中。@Data
:因为属性的装配注入,需要依赖于对应的get/set
方法。因此通过这个注解来快速实现get/set
。
TokenContext
类,用来存储userId
,这样当次请求就可以在任意一个地方拿到这个变量了。
public class TokenContext {
public static final ThreadLocal<Long> USER_ID = new ThreadLocal<>();
public static Long getUserId() {
return USER_ID.get();
}
public static void setUserId(Long userId) {
USER_ID.set(userId);
}
public static void clear(){
USER_ID.remove();
}
}
1.3 拦截器的注册
拦截器TokenInterceptor
:
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {
// 过滤登录等特殊请求地址,真实情况要比这个多的多,比如静态资源的等等。
// Header的key
public static final String LOGIN_URL = "/login";
public static final String TOKEN = "token";
@Autowired
private JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
// 排除登录等特殊请求
if (requestURI.contains(LOGIN_URL)) {
return true;
}
String token = request.getHeader(TOKEN);
if (StringUtils.isBlank(token)) {
throw new SignatureException("Token不能为空!");
}
// 获取JWT中存储的userId
Long userId = jwtUtil.getSubject(token);
if (userId == null) {
throw new SignatureException("Token失效,请重新登录!");
}
TokenContext.setUserId(userId);
return true;
}
}
WebMvc
配置WebConfig
:配置拦截器生效。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private TokenInterceptor tokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");
}
}
1.4 异常类处理和接口实现
写一个异常类处理ExceptionController
:
@RestControllerAdvice
public class ExceptionController {
@ExceptionHandler(value = {SignatureException.class})
@ResponseBody
public String authorizationException(SignatureException e) {
return e.getMessage();
}
}
写两个接口:
@RestController
public class MyController {
@Autowired
private JwtUtil jwtUtil;
@PostMapping("/login")
public String login(@RequestParam("userId") Long userId) {
String token = jwtUtil.buildToken(userId);
return "Success, Jwt Token : " + token;
}
@PostMapping("/getUser")
public String getUser() {
try {
Long userId = TokenContext.getUserId();
if (userId != null) {
return "成功拿到用户信息: " + userId;
}
return "用户信息为空";
} finally {
TokenContext.clear();
}
}
}
1.5 测试
登录结果如下:
测试带Token
请求:
测试不带Token
请求:
文章到这里就结束啦,后面会出一篇文章,整合Shiro + JWT + SpringCloud
(SpringBoot
)