项目场景:
SpringBoot拦截器获取token用户对象优雅地传递到Controller层
问题描述
后端有许多接口都需要请求中携带有正确的Token,这时采用拦截器来验证token,但是每个接口都还是需要解析一遍token,浪费资源,不免显得代码繁琐,臃肿。``
解决方案:
我们可以把用户信息存储起来,方便其它地方获取,两种方式:
- 使用ThreadLocal线程存储(推荐)
- 存储到上下文中request.setAttribute
1、自定义拦截器Interceptor
抛出的异常需要自己捕捉,返回
java
package com.test.aop;
import com.test.entity.TokenUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Token拦截器
*/
@Component
@Slf4j
public class TokenInterceptor implements HandlerInterceptor {
/**
* 存储用户信息
*/
private static ThreadLocal<TokenUserInfo> userThread = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从header中获取token
String token = request.getHeader(CommonConstant.ACCESS_TOKEN);
// 如果参数中不存在token,则报错
if (StringUtils.isBlank(token)) {
throw new RuntimeException("请求头缺少token参数");
}
try {
// TODO 根据token获取用户信息
// ......
}catch (Exception e){
log.error("获取用户信息失败:", e);
throw new RuntimeException("获取用户信息失败");
}
//模拟已经获取到了用户信息
TokenUserInfo tokenUserInfo = new TokenUserInfo();
tokenUserInfo.setUserId("1227086153ef415896da5819d4fb4c2f");
tokenUserInfo.setLoginAccount("test");
tokenUserInfo.setLoginUserName("测试");
//token失效
if(tokenUserInfo == null){
throw new RuntimeException("token失效");
}
/*
* 存储用户信息方便其它地方获取,两种方式
* 1.使用ThreadLocal线程存储(推荐)
* 2.存储到上下文中request.setAttribute
*/
// 放入线程域
userThread.set(tokenUserInfo);
//2.存储到上下文中request.setAttribute
// request.setAttribute("tokenUserInfo",tokenUserInfo);
//放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//程序运行结束之后,删除线程,防止内存泄漏
userThread.remove();
}
public static TokenUserInfo getUser() {
return userThread.get();
}
}
用户实体类
java
package com.test.entity;
import lombok.Data;
/**
* 用户信息
*
*/
@Data
public class TokenUserInfo {
/**
* 用户id
*/
private String userId;
/**
* 登录帐号
*/
private String loginAccount;
/**
* 用户名
*/
private String loginUserName;
}
2、配置拦截器
java
package com.test.config;
import com.test.aop.TokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* WebMvc配置
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* 解决跨域问题
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS").maxAge(3600);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/swagger/**").addResourceLocations("classpath:/static/swagger/");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
}
@Resource
private TokenInterceptor tokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//不拦截的地址
List<String> excludedList = new ArrayList<>();
//swagger地址
excludedList.add("/swagger-ui.html");
excludedList.add("/swagger-ui.html/**");
excludedList.add("/webjars/**");
excludedList.add("/swagger/**");
excludedList.add("/doc.html");
excludedList.add("/doc.html/**");
excludedList.add("/swagger-resources/**");
excludedList.add("/v2/**");
excludedList.add("/favicon.ico");
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")//拦截所有请求
.excludePathPatterns(excludedList);//排除的请求
super.addInterceptors(registry);
}
}
3、Controller层获取
java
package com.test.controller;
import com.test.aop.TokenInterceptor;
import com.test.entity.TokenUserInfo;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@RestController
@Api(tags = "测试接口")
@RequestMapping("/test")
public class TestController {
@RequestMapping(value = "/get", method = RequestMethod.GET)
public String get(HttpServletRequest request) {
/*
* 优雅的获取用户信息,两种方式
* 1.使用ThreadLocal线程存储(推荐)
* 2.存储到上下文中request.setAttribute
*/
TokenUserInfo tokenUserInfo = TokenInterceptor.getUser();
// TokenUserInfo tokenUserInfo = (TokenUserInfo) request.getAttribute(CommonConstant.USER_INFO);
return "success";
}
}