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

相关推荐
xcl092519 分钟前
宠物商城社交托运购物系统开发实战:从架构设计到完整实现指南
java·spring boot
没差c24 分钟前
RetrofitClient写接口、导包、配置、路径
java·spring boot·okhttp
RuoyiOffice1 小时前
SpringBoot+Vue3 节日主题换肤实战:一条参数换全站配色,节后自动还原
spring boot·vue3·spring boot 3·vben admin·ruoyi office·节日主题·换肤方案
XLYcmy1 小时前
大语言模型(LLM)核心技术梳理:从词元化到 RAG 的工程实践
自然语言处理·prompt·embedding·token·cot·tokenization·上下文工程
摇滚侠1 小时前
《Spring Boot 3:高级与架构设计》第 2 章 IOC容器的高级机制 Environment 个人理解 4
java·spring boot·笔记·后端
遨游DATA1 小时前
Spring 接口返回 errorMessage=null:如何排查二次异常覆盖原始业务错误
spring boot·异常处理·接口排障
+VX:Fegn08953 小时前
计算机毕业设计|基于springboot + vue图书借阅管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
szephyr3 小时前
前端鉴权实战:Token 存哪、怎么刷新、怎么防 XSS 与 CSRF
鉴权·xss·csrf·jwt·token
星光开发者3 小时前
基于Spring Boot的充电桩管理系统的设计与实现-计算机毕设【课程设计】57105
vue.js·spring boot·vscode·mysql·django·php·express
+VX:Fegn089513 小时前
计算机毕业设计|基于springboot + vue蛋糕店管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·课程设计