SpringMVC拦截器+ThreadLocal统一token处理标题
1、JWT工具类
package com.tanhua.commons.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private static final int TOKEN_TIME_OUT = 3_600;
private static final String TOKEN_SECRET = "itcast";
public static String getToken(Map params) {
long currentTime = System.currentTimeMillis();
return Jwts.builder()
.signWith(SignatureAlgorithm.HS512, TOKEN_SECRET)
.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000))
.addClaims(params)
.compact();
}
public static Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(TOKEN_SECRET)
.parseClaimsJws(token).getBody();
}
public static boolean verifyToken(String token) {
if (StringUtils.isEmpty(token)) {
return false;
}
try {
Claims claims = Jwts.parser()
.setSigningKey(TOKEN_SECRET)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
return false;
}
return true;
}
}
2、使用ThreadLocal封装获取请求用户信息的工具类
- 学习ThreadLocal类
- ThreadLocal类是线程独立的
- ThreadLocal用于存储从解析本次请求的token得到的用户信息,方便后续层次的使用,降低代码冗余量
- 工具类代码
package com.tanhua.server.interceptor;
import com.tanhua.model.domain.User;
public class UserHolder {
private static ThreadLocal<User> t1 = new ThreadLocal<>();
public static void set(User user) {
t1.set(user);
}
public static User get() {
return t1.get();
}
public static Long getUserId() {
return t1.get().getId();
}
public static String getMobile() {
return t1.get().getMobile();
}
public static void remove() {
t1.remove();
}
}
3、SpringMVC拦截器
package com.tanhua.server.interceptor;
import com.tanhua.commons.utils.JwtUtils;
import com.tanhua.model.domain.User;
import io.jsonwebtoken.Claims;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
boolean verifyToken = JwtUtils.verifyToken(token);
if (!verifyToken) {
response.setStatus(401);
return false;
}
Claims claims = JwtUtils.getClaims(token);
String mobile = (String) claims.get("mobile");
Integer id = (Integer) claims.get("id");
User user = new User();
user.setId(Long.valueOf(id));
user.setMobile(mobile);
UserHolder.set(user);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.remove();
}
}
- 创建拦截器类后,还要创建专门的配置类对拦截器进行配置,配置类与拦截器类处于同一包下
package com.tanhua.server.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/user/login", "/user/loginVerification");
}
}
4、总结
- 前端发起登录请求,登录成功后,后端返回一个封装了登录用户信息的token给前端,后续前端的请求必须携带这个token。
- 前端发起除登录以外的请求时,必须携带之前登录成功后后端返回的token,后端的拦截器会拦截这些请求并校验token。
- 校验token成功,放行并且保存了此次请求用户信息(从解析的token中获得),用于后续层次对请求的处理。
- 校验失败,拦截本次请求。
- 请求示意图如下