springboot通过threadLocal+参数解析器实现保存当前用户登录信息

首先先介绍一下threadLocal

ThreadLocal

        线程局部变量,创建一个线程变量后,针对这个变量可以让每个线程拥有自己的变量副本,每个线程是访问的自己的副本,与其他线程的相互独立。

大致知道threadLocal就可以了,然后我们直接看例子

首先从登录的地方走起,这个只是个简单的设备号登录,账号密码同理,到时候改改就行

@Service
public class UserLoginService {@Autowiredprivate UserService userService;@Resourceprivate UserLoginConverter userLoginConverter;@Resourceprivate UserConverter userConverter;@Transactional(rollbackFor = Exception.class)public UserLoginVO login(UserLoginDTO dto) {UserLoginVO result = new UserLoginVO(SignType.SIGNIN);User user = userService.findOne(dto.getDeviceNo());if (Objects.isNull(user)) {user = userService.save(userLoginConverter.dto2Dto(dto));result.setSignType(SignType.SIGNUP);}UserVO userVO = userConverter.entity2VO(user);result.setUser(userVO);result.setToken(UserTokenUtils.create(new UserToken(user.getId())));return result;}}

可以看见用户登录以后会制造一个token

UserTokenUtils工具类:
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class UserTokenUtils {private static final String ID = "id";private static final String ISSUER = "dreamland";private static final String TIMESTAMP = "timestamp";private static final Algorithm ALGORITHM = Algorithm.HMAC256("79cIYsXMF9TLDCPy");private static Long getExpireTime() {UserAuthProperties properties = SpringContextHolder.getBean(UserAuthProperties.class);return properties.getTokenExpire() * 1000L;}/*** 创建Token** @param token* @return*/public static String create(UserToken token) {Date expiresAt = new Date(System.currentTimeMillis() + getExpireTime());return JWT.create() //.withIssuer(ISSUER) // jwt签发者,可选.withExpiresAt(expiresAt) // 过期时间.withClaim(ID, token.getId()) // id.withClaim(TIMESTAMP, token.getTimestamp()) // 时间戳.sign(ALGORITHM);}/*** 验证Token** @param token* @return*/public static UserToken verify(String token) {UserToken info = new UserToken();JWTVerifier verifier = JWT.require(ALGORITHM) // 指定验证算法.withIssuer(ISSUER) // 要求token必须有指定签发者.build();DecodedJWT decode = verifier.verify(token);info.setId(decode.getClaim(ID).asLong());info.setTimestamp(decode.getClaim(TIMESTAMP).asLong());return info;}/*** 通过Token解码** @param token* @return*/public static UserToken decode(String token) {UserToken info = new UserToken();DecodedJWT decode = JWT.decode(token);info.setId(decode.getClaim(ID).asLong());info.setTimestamp(decode.getClaim(TIMESTAMP).asLong());return info;}}

UserToken 类:

@Data
@NoArgsConstructor
public class UserToken {private Long id;private Long timestamp = System.currentTimeMillis();public UserToken(Long userId) {this.id = userId;}}

我使用的是jwt,因此需要导入一下这个pom依赖

            <dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>${jwt.version}</version></dependency>

至此token已经传给了前端,前端每次发送请求都会带上我们的token

然后就需要在拦截器里面做手脚了

UserAuthContextInterceptor拦截器类:
public class UserAuthContextInterceptor extends HandlerInterceptorAdapter {/*** 前置拦截*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = null;// 从请求参数中获取tokentoken = request.getParameter(UserConsts.HEADER_USER_TOKEN);// 如果为空从Cookie中获取Cookie[] cookies = Optional.ofNullable(request.getCookies()).orElse(new Cookie[]{});List<Cookie> cookieList = Arrays.asList(cookies).stream().filter(cookie -> UserConsts.HEADER_USER_TOKEN.equals(cookie.getName())).collect(Collectors.toList());Cookie cookie = cookieList.isEmpty() ? null : cookieList.get(0);token = StringUtils.isBlank(token) && Objects.nonNull(cookie) ? cookie.getValue() : token;// 如果为空从head中获取token = StringUtils.isBlank(token) ? request.getHeader(UserConsts.HEADER_USER_TOKEN) : token;if (StringUtils.isBlank(token)) {throw new JWTVerificationException("The token can not be empty!");}// 解析Token信息,并存入UserAuthContextUserToken userTokenInfo = UserTokenUtils.verify(token);UserAuthContext.setUserTokenInfo(userTokenInfo);return true;}/*** 后置拦截*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {UserAuthContext.release();}}

可以看见我们在前置拦截器和后置拦截器里面分别调用了

UserAuthContext.setUserTokenInfo(userTokenInfo);  设置当前请求用户的token信息到线程上下文
UserAuthContext.release(); 释放当前线程上下文的用户信息

拦截器会把发来的带有token的请求解析并且存储,然后会在返回响应时将解析存储的用户信息给清理掉,真是万花丛中走片叶不沾身啊。

UserAuthContext类:
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class UserAuthContext {private static final ThreadLocal<UserToken> CONTEXT = new ThreadLocal<>();/*** 获取用户id*/public static Long getId() {return CONTEXT.get().getId();}/*** 获取timestamp*/public static Long getTimestamp() {return CONTEXT.get().getTimestamp();}/*** 设置当前请求用户的token信息到线程上下文*/public static void setUserTokenInfo(UserToken info) {CONTEXT.set(info);}/*** 释放当前线程上下文的用户信息*/public static void release() {CONTEXT.remove();}}

这个类里面就包含了存入已经撤销的命令,并且还有获取用户信息的命令,我这里仅仅有用户id

的获取方法

一切准备就绪,但是这样我们自定义的拦截器并不会生效,也就是请求压根不会过我们的拦截器,所以就需要手动配置使用

UserAuthConfig类:
@Configuration
@EnableConfigurationProperties(UserAuthProperties.class)
public class UserAuthConfig implements WebMvcConfigurer {@Autowiredprivate UserAuthProperties userAuthProperties;@Beanpublic UserAuthContextInterceptor userActionContextInterceptor() {return new UserAuthContextInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(userActionContextInterceptor()).addPathPatterns(userAuthProperties.getPathPatterns()).excludePathPatterns(userAuthProperties.getExcludePathPatterns());}/*** 静态资源映射*/@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/swagger-ui/index.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/doc.html/**").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");}}

在拦截器配置里面将我们的拦截器加入使用,同时指定了要拦截和不拦截的路径,不过路径我又封装在了UserAuthProperties类中了

UserAuthProperties类:
@Data
@ConfigurationProperties("user-auth")
public class UserAuthProperties {/*** token过期时间(单位:秒) 默认7天*/private Long tokenExpire;/*** 要拦截的路径*/private String[] pathPatterns;/*** 不拦截的路径*/private String[] excludePathPatterns;}

可能有些人会疑惑,这个过期时间没咋个看见用,其实是用了的,不过我忘记说了,fack。

已经在UserTokenUtils创建token的时候调用了一个getExpireTime()方法,里面有一句是这样的:
UserAuthProperties properties = SpringContextHolder.getBean(UserAuthProperties.class);
return properties.getTokenExpire() * 1000L;

通过 SpringContextHolder 工具类获取名为 UserAuthProperties 的 Bean 对象。并且拿到里面的tokenExpire值,看,是不是重新连起来了

这个类也放出来

SpringContextHolder类:
@Configuration
public class SpringContextHolder implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {context = applicationContext;}public static Object getBean(String beanName) {checkContext();return context.getBean(beanName);}public static <T> T getBean(String beanName, Class<T> clazz) {checkContext();return context.getBean(beanName, clazz);}public static <T> T getBean(Class<T> clazz) {checkContext();return context.getBean(clazz);}private static void checkContext() {if (Objects.isNull(context)) {throw new RuntimeException("The applicationContext is not initialized!");}}
}

重新回归正题,那我们的路径是从UserAuthProperties类中拿的,那这个类得数据又是哪里来的?其实UserAuthProperties类中有个注解@ConfigurationProperties("user-auth"),就是从我们得配置文件得user-auth中读取数据

我的application.xml:

server:# 开启优雅关闭shutdown: graceful # gzip压缩compression:enabled: truemin-response-size: 2048mime-types: text/plain,text/xml,application/xml,application/jsonport: 8160
spring:lifecycle: # 关闭的缓冲时间timeout-per-shutdown-phase: 30s application:name: iverify-clientprofiles:active: localservlet:multipart:max-file-size: 50MBmax-request-size: 100MB
user-auth:token-expire: 31536000path-patterns:- /v1/**exclude-path-patterns:- /swagger-resources/**- /webjars/**- /v2/**- /swagger-ui.html/**- /doc.html/**- /error- /favicon.ico- /v1/health- /v1/user-login/device- /v1/face-compare-results/watermark- /v1/user/queryUser- /v1/avatar-compare-results/query

其他配置不用关注,只看user-auth就好,至此数据顺利过通。

小小总结一下,其实就是用户登录得时候创建了一个token,里面包含了用户得信息。我们后端在用户登录成功后把token放给前端,前端拿着token访问我们接口。访问接口得请求就会被我们得拦截器拦住,拦截器对token进行解析和存入threadLocal中,在请求走完通过后置拦截器来注销保存得用户信息。

使用:

直接调用getId()就可以轻松拿到用户id了,方方便便轻轻松松

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/780069.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt 完成图片的缩放拖动

1. 事件和函数 主要使用事件paintEvent(QPaintEvent *event)和drawTiledPixmap函数实现绘图。 paintEvent事件在改变窗口大小、移动窗口、手动调用update等情形下会被调用。需先了解下绘图该函数的用法。 - QPainter::drawTiledPixmap(int x, int y, int w, int h, const QPi…

Docker部署MongoDB+整合Mongo版MyBatis—Plus

&#x1f469;&#x1f3fd;‍&#x1f4bb;个人主页&#xff1a;阿木木AEcru &#x1f525; 系列专栏&#xff1a;《Docker容器化部署系列》 《Java每日面筋》 &#x1f4b9;每一次技术突破&#xff0c;都是对自我能力的挑战和超越。 目录 一、 MongoDB简介1.1 适用场景1.2 应…

缓冲区溢出漏洞学习总结(漏洞原理及其利用方法)

文章目录 前言1、缓冲区溢出漏洞概述1.1、漏洞概述1.2、缓冲区溢出漏洞概述1.3、缓冲区溢出攻击概述1.4、引发缓冲区溢出的原因 2、栈溢出漏洞2.1、栈溢出漏洞概述2.2、栈溢出漏洞利用2.2.1、利用方法一&#xff1a;修改返回地址2.2.2、利用方法二&#xff1a;覆盖临接变量 3、…

(js)循环条件满足时终止循环

(js)循环条件满足时终止循环 功能需求&#xff1a;勾选的字段中若包含“数据标注划分”则显示数据划分&#xff0c;不包含则不显示 包含&#xff1a; 不包含&#xff1a; // 标注划分显示 const markStr 数据标注划分 for (let i 0; i < value.length; i) { //value为勾选…

Coursera自然语言处理专项课程03:Natural Language Processing with Sequence Models笔记 Week02

Natural Language Processing with Sequence Models Course Certificate 本文是https://www.coursera.org/learn/sequence-models-in-nlp 这门课程的学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 文章目录 Natural Language Processing with Sequence ModelsWeek 02…

vsqt更改ui,cpp报错(唯二)解决方法,及ui界面布局在cpp文件的运用基本流程

qt的ui布局界面如下 点cpp文件->编译 此时就会自动生成ui_xxx.h 这里是ui文件里面就有类名&#xff1a;Ui_文件名字 下面就有一个类继承于这个类 你所使用的这个ui指针&#xff0c;就这么来的 ***报错解决方法有两种&#xff1a;***第一种&#xff1a;如果改了ui&#x…

钉钉服务端API报错 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确

problem 调用钉钉服务端API&#xff0c;机器人发送群聊消息&#xff0c;后台返回报错信息: 钉钉服务端API报错 错误描述: robot 不存在&#xff1b;解决方案:请确认 robotCode 是否正确&#xff1b; reason 定位: 登录后台&#xff0c;查看机器人是存在查看机器人调用权限接…

Django创建多app应用

目录 1. 引言 2. 多app创建的两种方式 2.1 多个app结构 2.2 单个apps多个app 3. 最后 1. 引言 在平常业务开发中&#xff0c;我们遇到的功能可能会有很多&#xff0c;单个app的应用可能无法满足我们 这个时候&#xff0c;我们就需要多app应用&#xff0c;例如&#xff1a…

linux 组建raid5详细操作

raid5最多运行损坏一个盘&#xff0c;最少3个盘&#xff0c;容量为少一块硬盘的容量之和。 如果硬盘数量较多&#xff0c;比如8块以上&#xff0c;建议用raid6&#xff0c;raid6最多允许两块硬盘损坏。 如果需要 一、安装raid软件 deb包 apt-get install mdadm或dnf包 dnf …

ElasticSearch学习篇11_ANNS之基于图的NSW、HNSW算法

前言 往期博客ElasticSearch学习篇9_文本相似度计算方法现状以及基于改进的 Jaccard 算法代码实现与效果测评_elasticsearch 文字相似度实现方法-CSDN博客 根据论文对文本相似搜索现状做了一个简要总结&#xff0c;然后对论文提到的改进杰卡德算法做了实现&#xff0c;并结合业…

蓝桥备赛——堆队列

AC code import os import sys import heapq a [] b [] n,k map(int,input().split())for _ in range(n):x,y map(int,input().split())a.append(x)b.append(y) q []# 第一种情况&#xff1a;不打第n个怪兽# 将前n-1个第一次所需能量加入堆 for i in range(n-1):heapq.h…

用xshell或ftp连接本地虚拟机linux系统,centos7修改动态ip地址

如果不知道怎么下载vm本地虚拟机软件或者不知道怎么安装可以参考我上一篇博客 vmWare虚拟机下载安装详细教程,手把手一步一步教学-CSDN博客 安装好虚拟机软件我们想要通过xshell和ftp工具来管理,小黑框不太舒服哈哈哈 一.准备工作 输入命令来查看当前的ip地址 ip addr 可以…

HarmonyOS 应用开发之PageAbility的启动模式

启动模式对应PageAbility被启动时的行为&#xff0c;支持单实例模式、多实例模式两种启动模式。 表1 PageAbility的启动模式 应用开发者可在config.json配置文件中通过“launchType”配置启动模式。示例如下&#xff1a; {"module": {..."abilities": [{…

上位机图像处理和嵌入式模块部署(qmacvisual透视变换)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 说到透视变换&#xff0c;以前我也不明白为什么有这样一个需求。后来在tier1做车道线检测的时候&#xff0c;才知道如果把camera拍摄到的图像做一次…

Delphi模式编程

文章目录 Delphi模式编程涉及以下几个关键方面&#xff1a;**设计模式的应用****Delphi特性的利用****实际开发中的实践** Delphi模式编程的实例 Delphi模式编程是指在使用Delphi这一集成开发环境&#xff08;IDE&#xff09;和Object Pascal语言进行软件开发时&#xff0c;采用…

PHP运行的注意事项和基本语法规范

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

JavaSE day15 笔记

第十五天课堂笔记 数组 可变长参数★★★ 方法 : 返回值类型 方法名(参数类型 参数名 , 参数类型 … 可变长参数名){}方法体 : 变长参数 相当于一个数组一个数组最多只能有一个可变长参数, 并放到列表的最后parameter : 方法参数 数组相关算法★★ 冒泡排序 由小到大: 从前…

Linux网络配置(超详细)

Linux网络配置大全 Linux网络配置一.网络地址配置网络地址查看–ifconfig使用网络配置命令设置网络接口参数-ifconfig禁用(临时)或者重新激活网卡设置虚拟网络接口 修改网络配置文件网络接口配置文件 IP命令详解OPTIONS选项OBJECT对象 ip link 二、获取和修改主机名hostname查看…

java电话号码的字母组合(力扣Leetcode17)

电话号码的字母组合 力扣原题链接 问题描述 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 示例 1&#xff1a;…

谷粒商城实战(007 压力测试)

Java项目《谷粒商城》架构师级Java项目实战&#xff0c;对标阿里P6-P7&#xff0c;全网最强 总时长 104:45:00 共408P 此文章包含第141p-第p150的内容 简介 安装jmeter 安装jmeter 使用中文 这样写就是200个线程循环100次 一共是2万个请求 介绍线程组 添加请求 可以是htt…