需求很简单,就是Controller层不需要再从request中获取请求头的token,再根据token去redis或者数据库获取用户信息判断用户是否登录。怎么实现呢?
1、先实现一个未授权的异常
当然这个异常可有可无,这里只是为了后续在拦截器中如果检测不通过就直接抛出这个异常
public class UnauthorizedException extends RuntimeException {public UnauthorizedException(String message) {super(message);}}
2、实现一个拦截器
这里就是根据请求头的token从redis获取用户信息设置到上下文中
@Componentpublic class TokenInterceptor implements HandlerInterceptor {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) {String token = request.getHeader("Authorization");if (StringUtils.isEmpty(token)) {throw new UnauthorizedException("缺少认证令牌");}String userId = JwtUtil.parseToken(token);String redisKey = "user:info:" + userId;UserDTO user = (UserDTO) redisTemplate.opsForValue().get(redisKey);UserContext.setCurrentUser(user);return true;}@Overridepublic void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler, Exception ex) {UserContext.remove(); // 防止内存泄漏}}
3、实现一个上下文
这里就简单的用ThreadLocal来实现即可,要注意在拦截器中把这个线程remove掉。
public class UserContext {private static final ThreadLocal<UserDTO> USER_HOLDER = new ThreadLocal<>();public static void setCurrentUser(UserDTO user) {USER_HOLDER.set(user);}public static UserDTO getCurrentUser() {return USER_HOLDER.get();}public static void remove() {USER_HOLDER.remove();}}
4、配置拦截器生效
@Configurationpublic class WebMvcConfig implements WebMvcConfigurer {@Autowiredprivate TokenInterceptor tokenInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(tokenInterceptor).addPathPatterns("/**").excludePathPatterns("/login");}}
这里一定要注意,里面配置的路径不能包括springboot配置文件的server.servlet.context-path.
5、这里再实现一个全局异常
@RestControllerAdvicepublic class GlobalExceptionHandler {@ExceptionHandler(UnauthorizedException.class)public ResponseEntity<Result<String>> handleUnauthorized(UnauthorizedException e) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Result.error(401, e.getMessage()));}@ExceptionHandler(Exception.class)public ResponseEntity<Result<String>> handleException(Exception e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, "服务器异常"));}}
这样就好了,是不是很简单。
