SpringBoot拦截器获取token用户对象优雅地传递到Controller层

项目场景:

SpringBoot拦截器获取token用户对象优雅地传递到Controller层


问题描述

后端有许多接口都需要请求中携带有正确的Token,这时采用拦截器来验证token,但是每个接口都还是需要解析一遍token,浪费资源,不免显得代码繁琐,臃肿。``


解决方案:

我们可以把用户信息存储起来,方便其它地方获取,两种方式:

  1. 使用ThreadLocal线程存储(推荐)
  2. 存储到上下文中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";
	}
}

参考源码:https://download.csdn.net/download/u011974797/85849196

相关推荐
Minyy112 小时前
SpringBoot程序的创建以及特点,配置文件,LogBack记录日志,配置过滤器、拦截器、全局异常
xml·java·spring boot·后端·spring·mybatis·logback
武昌库里写JAVA2 小时前
39.剖析无处不在的数据结构
java·vue.js·spring boot·课程设计·宠物管理
李白的粉8 小时前
基于springboot的在线教育系统
java·spring boot·毕业设计·课程设计·在线教育系统·源代码
小马爱打代码9 小时前
SpringBoot原生实现分布式MapReduce计算
spring boot·分布式·mapreduce
iuyou️9 小时前
Spring Boot知识点详解
java·spring boot·后端
一弓虽9 小时前
SpringBoot 学习
java·spring boot·后端·学习
来自星星的猫教授11 小时前
spring,spring boot, spring cloud三者区别
spring boot·spring·spring cloud
乌夷12 小时前
使用spring boot vue 上传mp4转码为dash并播放
vue.js·spring boot·dash
A阳俊yi14 小时前
Spring Boot日志配置
java·spring boot·后端
苹果酱056714 小时前
2020-06-23 暑期学习日更计划(机器学习入门之路(资源汇总)+概率论)
java·vue.js·spring boot·mysql·课程设计