• [SpringBoot-vue3]用户登录实现JWT单点登录/ThreadLocal保存用户信息


     ✨✨个人主页:沫洺的主页

    📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏 

                               📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专栏

                               📖Docker专栏📖Reids专栏📖MQ专栏📖SpringCloud专栏     

    💖💖如果文章对你有所帮助请留下三连✨✨

    🍁效果图

    简单的用户登录注册环境的搭建,目的是使用JWT(Json web token)来模拟分布式站点的单点登录(SSO)场景

    这里模拟了三种token的情况

    🌾基于Token的身份验证步骤

    • 客户端使用用户名跟密码请求登录;
    • 服务端收到请求,去验证用户名与密码;
    • 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端;
    • 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里;
    • 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token;
    • 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据。

    🌱JWT介绍

    原文链接: 什么是 JWT -- JSON WEB TOKEN

    Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

    传统的session认证:

    • 当用户经过应用认证之后,应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
    • 如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
    • 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

    基于token的鉴权机制:

    • 基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。

    JWT详解参考: JWT详解

    总结来说就是服务无状态化,服务器不保存任何状态,而是客户端将状态带过来这种机制

    🌴JWT工具使用

    Hutool工具: JWTUtil使用

    JSON Web Token 入门教程

    🍂代码实现

    建表

    1. CREATE TABLE `scm_user` (
    2. `id` int NOT NULL AUTO_INCREMENT,
    3. `tel` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
    4. `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
    5. `nickName` varchar(255) DEFAULT NULL,
    6. `lastUpdateTime` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    7. PRIMARY KEY (`id`)
    8. ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;;

    🌿后端接口搭建及返回token

    • 客户端使用用户名跟密码请求登录;
    • 服务端收到请求,去验证用户名与密码;
    • 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端;

    application.yml

    1. #数据库连接信息配置
    2. spring:
    3. datasource:
    4. driver-class-name: com.mysql.cj.jdbc.Driver
    5. url: jdbc:mysql://localhost:3306/db5?useSSL=false&useServerPrepStmts=true&allowPublicKeyRetrieval=true
    6. username: root
    7. password: 123456
    8. druid:
    9. initial-size: 10 # 初始化时建立物理连接的个数
    10. min-idle: 10 # 最小连接池数量
    11. maxActive: 200 # 最大连接池数量
    12. maxWait: 60000 # 获取连接时最大等待时间,单位毫秒
    13. #映射文件所在位置
    14. #mybatis:
    15. # mapper-locations: classpath:mapper/*Mapper.xml
    16. # #别名
    17. # type-aliases-package: com.moming.entity
    18. mybatis-plus:
    19. config-locations: classpath:mapper/*Mapper.xml
    20. #别名
    21. type-aliases-package: com.moming.entity
    22. #配置日志级别
    23. logging:
    24. level:
    25. # root: error
    26. com.moming: info
    27. rest:
    28. controller:
    29. advice:
    30. base-packages: com.moming.controller
    31. #配置authority-spring-boot-starter相关信息
    32. moming:
    33. authority:
    34. enable: true
    35. path-patterns: /api/**
    36. exclude-path-patterns: /api/user/*
    37. #设置token密钥
    38. token:
    39. secret: moming

    UserDto

    1. @Data
    2. public class UserDto{
    3. private Integer id;
    4. private String tel;
    5. private String nickName;
    6. private String password;
    7. }

    UserQueryDto

    1. @Data
    2. public class UserQueryDto{
    3. private Integer id;
    4. private String tel;
    5. private String password;
    6. }

    UserLoginDto

    1. @Data
    2. public class UserLoginDto {
    3. private String tel;
    4. private String password;
    5. }

    UserEntity

    1. @Data
    2. public class UserEntity {
    3. private Integer id;
    4. private String tel;
    5. private String nickName;
    6. private String password;
    7. private LocalDateTime lastUpdateTime;
    8. }

    UserMapper

    1. @Mapper
    2. public interface UserMapper {
    3. List select(UserQueryDto dto);
    4. int insert(UserEntity entity);
    5. int update(UserEntity entity);
    6. int delete(Integer id);
    7. }

    UserMapper.xml

    1. "1.0" encoding="UTF-8"?>
    2. mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    3. <mapper namespace="com.moming.dao.UserMapper">
    4. <resultMap id="userMap" type="com.moming.entity.UserEntity">
    5. <result property="id" column="id"/>
    6. <result property="tel" column="tel"/>
    7. <result property="nickName" column="nickName"/>
    8. <result property="password" column="password"/>
    9. <result property="lastUpdateTime" column="lastUpdateTime"/>
    10. resultMap>
    11. <select id="select" resultMap="userMap" >
    12. select * from scm_user where 1 = 1
    13. <if test="tel != null">
    14. and tel = #{tel}
    15. if>
    16. <if test="password != null">
    17. and password = #{password}
    18. if>
    19. order by id desc
    20. select>
    21. <insert id="insert" parameterType="com.moming.entity.UserEntity">
    22. insert into scm_user ( tel,nickName, password)
    23. values ( #{tel}, #{nickName}, #{password} )
    24. insert>
    25. <update id="update" parameterType="com.moming.entity.UserEntity">
    26. update scm_user
    27. <set>
    28. <if test="tel != null">
    29. tel = #{tel},
    30. if>
    31. <if test="password != null">
    32. password = #{password},
    33. if>
    34. set>
    35. where id = #{id}
    36. update>
    37. <delete id="delete">
    38. delete from scm_user where id = #{id}
    39. delete>
    40. mapper>

    UserService

    1. @Service
    2. public class UserService {
    3. @Autowired
    4. private UserMapper userMapper;
    5. public Integer insert(UserDto dto){
    6. //业务判断逻辑
    7. UserEntity entity = BeanUtil.copyProperties(dto, UserEntity.class);
    8. return userMapper.insert(entity);
    9. }
    10. public Integer update(UserDto dto){
    11. UserEntity entity = BeanUtil.copyProperties(dto, UserEntity.class);
    12. return userMapper.update(entity);
    13. }
    14. public List select(UserQueryDto dto){
    15. List entities = userMapper.select(dto);
    16. return BeanUtil.copyToList(entities,UserDto.class);
    17. }
    18. }

    UserController

    1. @RestController
    2. @RequestMapping("/api/user")
    3. public class UserController {
    4. @Autowired
    5. private UserService userService;
    6. //密钥
    7. @Value("${moming.token.secret}")
    8. private String TOKEN_SECRET;
    9. @PutMapping
    10. public Integer update(@RequestBody UserDto dto){
    11. return userService.update(dto);
    12. }
    13. @PostMapping("/regist")
    14. public Integer insert(@RequestBody UserDto dto){
    15. return userService.insert(dto);
    16. }
    17. @GetMapping
    18. public List select(UserQueryDto dto){
    19. return userService.select(dto);
    20. }
    21. @PostMapping("/login")
    22. public String login(@RequestBody UserLoginDto dto) throws Exception {
    23. //业务判断
    24. //获取请求头数据是否为空
    25. if(ObjectUtil.isEmpty(dto.getTel())){
    26. throw new Exception("手机号不能为空");
    27. }
    28. if(ObjectUtil.isEmpty(dto.getPassword())){
    29. throw new Exception("密码不能为空");
    30. }
    31. //不为空封装到对象中
    32. UserQueryDto queryDto = new UserQueryDto();
    33. queryDto.setTel(dto.getTel());
    34. queryDto.setPassword(dto.getPassword());
    35. //查询数据库中是否匹配登录信息
    36. List userList = userService.select(queryDto);
    37. //如果查出来的是空的,则表示没有匹配上,登录失败
    38. if(ObjectUtil.isEmpty(userList)){
    39. throw new Exception("手机号或者密码错误");
    40. }
    41. //匹配上,由于是数组对象,取第一个
    42. UserDto user = userList.get(0);
    43. //创建JWT
    44. Map map = new HashMap() ;
    45. //用户id
    46. map.put("userId", user.getId());
    47. //用户手机号
    48. //String tel = user.getTel();
    49. //tel = tel.substring(0,3)+"******"+tel.substring(9,11);
    50. //map.put("tel", tel);
    51. map.put("tel", user.getTel());
    52. //用户别名
    53. map.put("nickName", user.getNickName());
    54. //过期时间
    55. map.put("expire_time", System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 15);
    56. //生成token
    57. String token = JWTUtil.createToken(map, TOKEN_SECRET.getBytes());
    58. return token;
    59. }
    60. }

    当登录接口成功通过业务判断,会返回一个加密的token,格式如下

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHBpcmVfdGltZSI6MTY2ODIzMDgyNTIyMCwidGVsIjoiMTIzKioqKioqMTAiLCJ1c2VySWQiOjF9.t1E8YfUuMVWpCfAtIw_i4MWZftSpr8tmlGTHphuv-Do

    在线解析JWT加密

    🍃前端实现接口及解析token

    • 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里;

    vue token解析: 解析token中携带的数据

    安装插件

    npm install jwt-decode --save

    在需要使用的地方引入

    1. import jwtDecode from 'jwt-decode'
    2. const code = jwtDecode(res.data.data.accessToken)
    3. console.log(code)// 就可以解析成功了

    部分核心代码如下

    UserLogin.vue

    UserRegist.vue

    App.vue

    1. <script setup lang="ts">
    2. import Menu from '@/components/Menu.vue'
    3. import { ref, onMounted } from 'vue'
    4. import PubSub from 'pubsub-js'
    5. import NavHead from '@/components/NavHead.vue'
    6. import { useRoute, useRouter } from 'vue-router'
    7. import { appStore } from "@/store/appStore"
    8. import { storeToRefs } from 'pinia'
    9. import jwtDecode from 'jwt-decode'
    10. let { userInfo } = storeToRefs(appStore());
    11. // 导入路由
    12. //当前路由
    13. const route = useRoute()
    14. //路由管理器
    15. const router = useRouter()
    16. const menuWidth = ref("200px");
    17. //点击事件,退出系统跳转登录页面
    18. const logout = () => {
    19. userInfo.value.tel = "游客"
    20. userInfo.value.userId = 0
    21. userInfo.value.token = ""
    22. router.push({ name: "login" })
    23. }
    24. onMounted(() => {
    25. //消费者 订阅消息
    26. PubSub.subscribe('menuopenorclose', (topic: string, data: any) => {
    27. //通过三元表达式动态设置宽度
    28. menuWidth.value = data == true ? "64px" : "200px"
    29. })
    30. // 接收登录成功的消息事件
    31. PubSub.subscribe('login-ok', (topic: string, data: any) => {
    32. // console.log(topic,data);
    33. let token = data;
    34. //解析token(JWT数据结构)
    35. const tokenObject:any = jwtDecode(token)
    36. //存入本地存储
    37. userInfo.value.token = token
    38. userInfo.value.userId = tokenObject.userId
    39. userInfo.value.tel = tokenObject.tel
    40. userInfo.value.nickName = tokenObject.nickName
    41. })
    42. })
    43. script>
    44. <style scoped>
    45. .el-row {
    46. align-items: center;
    47. vertical-align: middle;
    48. padding-top: 10px;
    49. }
    50. .el-header {
    51. background: linear-gradient(135deg, #feac5e, #c779d0, #4bc0c8);
    52. }
    53. .logo {
    54. width: 50px;
    55. height: 45px;
    56. text-align: left;
    57. }
    58. .col1 {
    59. font: oblique bolder 36px "华文彩云";
    60. font-weight: bold;
    61. /* color: black; */
    62. background-image: linear-gradient(to right, rgb(185, 244, 108), rgb(195, 173, 242));
    63. -webkit-background-clip: text;
    64. color: transparent;
    65. font-size: 30px;
    66. }
    67. .el-main {
    68. --el-main-padding: 0px;
    69. }
    70. .userinfo {
    71. text-align: right;
    72. }
    73. .el-dropdown-link {
    74. color: white;
    75. }
    76. .el-main {
    77. background: linear-gradient(135deg, #f7f0ac, #acf7f0, #f0acf7);
    78. }
    79. style>

    appStore.ts

    1. import { defineStore } from 'pinia'
    2. export const appStore = defineStore({
    3. id: 'scmapp',
    4. state: () => {
    5. return {
    6. userInfo:{
    7. userId: 0,
    8. tel: "",
    9. nickName: "",
    10. token: "",
    11. },
    12. menuCollapse: false,
    13. //定义一个editableTabs数组,里边放初始化元素
    14. tabList: [{
    15. title: '主页',
    16. name: 'home',
    17. close: false,
    18. }],
    19. //当前活动标签,默认显示name:'home'的元素
    20. activeTabName: "home",
    21. }
    22. },
    23. getters: {
    24. },
    25. actions: {
    26. },
    27. // 开启数据缓存
    28. persist: {
    29. enabled: true,
    30. strategies: [
    31. {
    32. key: 'scm_app',
    33. storage: localStorage,
    34. }
    35. ]
    36. }
    37. })

    将用户登录不重要的信息通过pinia保存到本地存储Local Storage中,通过消息的订阅与发布这种通信模式,登录成功后UserLogin.vue作为发布者将token发给订阅者,通知App.vue订阅者去解析token

    1. //发布者,发送消息
    2. PubSub.publish('login-ok', token)
    3. //订阅者接收登录成功的消息事件
    4. PubSub.subscribe('login-ok', (topic: string, data: any) => {
    5. // console.log(topic,data);
    6. let token = data;
    7. //解析token(JWT数据结构)
    8. const tokenObject:any = jwtDecode(token)
    9. //存入本地存储
    10. userInfo.value.token = token
    11. userInfo.value.userId = tokenObject.userId
    12. userInfo.value.tel = tokenObject.tel
    13. userInfo.value.nickName = tokenObject.nickName
    14. })

    🌻前端拦截器请求头存放token

    JSON Web Token 入门教程

     http/index.ts

    1. import axios, { AxiosRequestConfig, AxiosRequestHeaders, AxiosResponse, } from "axios";
    2. //弹窗图标和加载图标
    3. import { ElMessage, ElLoading } from "element-plus";
    4. // import { userInfo } from "os";
    5. import { appStore } from "@/store/appStore"
    6. import { storeToRefs } from 'pinia'
    7. let { userInfo } = storeToRefs(appStore());
    8. const state = {
    9. ok: 0,//请求成功状态码
    10. 401: "ERR_BAD_REQUEST"
    11. };
    12. //返回数据规则
    13. interface IResponseData {
    14. status: number;
    15. message?: string;
    16. data: T;
    17. code: string;
    18. }
    19. //默认配置,封装了一个实例对象
    20. const config = {
    21. baseURL: "",
    22. timeout: 30 * 1000,
    23. withCredentials: true,
    24. };
    25. let loading: any = null;
    26. //类似定义一个类
    27. class Http {
    28. axiosInstance; //定义了一个axiosInstance属性,未来它放的是一个axios实例
    29. constructor(config: any) {
    30. //实例化请求配置
    31. this.axiosInstance = axios.create(config);
    32. // 添加请求拦截器
    33. this.axiosInstance.interceptors.request.use(
    34. //在发送请求之前做些什么
    35. (config: AxiosRequestConfig) => {
    36. //弄了一个加载的过度
    37. loading = ElLoading.service({
    38. lock: true,
    39. text: '加载中...',
    40. background: 'rgba(0, 0, 0, 0.7)',
    41. //覆盖整个屏幕
    42. fullscreen: true
    43. })
    44. //处理token
    45. if (userInfo.value.token != "") {
    46. let headers = config.headers as AxiosRequestHeaders
    47. headers.Authorization = userInfo.value.token
    48. }
    49. return config;
    50. },
    51. (error: any) => {
    52. loading.close();
    53. // 对请求错误做些什么
    54. return Promise.reject(error);
    55. }
    56. );
    57. // 添加响应拦截器
    58. this.axiosInstance.interceptors.response.use(function (response) {
    59. // 对响应数据做点什么
    60. loading.close();
    61. let apiData = response.data;
    62. // console.log(apiData)
    63. // console.log(apiData.data)
    64. //将apiData的属性取出来
    65. const { code, message, data } = apiData;
    66. //取出来之后处理属性
    67. if (code === undefined) {
    68. return apiData;
    69. } else if (code === 0) {
    70. return data;
    71. } else {
    72. ElMessage.error(message)
    73. }
    74. return apiData.data;
    75. }, function (error) {
    76. // 对响应错误做点什么
    77. loading.close();
    78. return Promise.reject(error);
    79. });
    80. }
    81. get(url: string, params?: object, data = {}): Promise<IResponseData> {
    82. return this.axiosInstance.get(url, { params, ...data });
    83. }
    84. post(url: string, params?: object, data = {}): Promise<IResponseData> {
    85. return this.axiosInstance.post(url, params, data);
    86. }
    87. put(url: string, params?: object, data = {}): Promise<IResponseData> {
    88. return this.axiosInstance.put(url, params, data);
    89. }
    90. patch(url: string, params?: object, data = {}): Promise<IResponseData> {
    91. return this.axiosInstance.patch(url, params, data);
    92. }
    93. delete(url: string, params?: object, data = {}): Promise<IResponseData> {
    94. return this.axiosInstance.delete(url, { params, ...data });
    95. }
    96. }
    97. //类似new了一个实例
    98. export default new Http(config);

    请求拦截器

    将token放到请求头的Authorization中

    1. // 添加请求拦截器
    2. this.axiosInstance.interceptors.request.use(
    3. //在发送请求之前做些什么
    4. (config: AxiosRequestConfig) => {
    5. //弄了一个加载的过度
    6. loading = ElLoading.service({
    7. lock: true,
    8. text: '加载中...',
    9. background: 'rgba(0, 0, 0, 0.7)',
    10. //覆盖整个屏幕
    11. fullscreen: true
    12. })
    13. //处理token
    14. if (userInfo.value.token != "") {
    15. //拿到请求头
    16. let headers = config.headers as AxiosRequestHeaders
    17. //赋值
    18. headers.Authorization = userInfo.value.token
    19. }
    20. return config;
    21. },
    22. (error: any) => {
    23. loading.close();
    24. // 对请求错误做些什么
    25. return Promise.reject(error);
    26. }
    27. );

    💐后端拦截器验证/解析token,通过ThreadLocal保存用户信息

    • 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token;
    • 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据。

    拦截器创建参考: 自定义authority-spring-boot-starter

    引入坐标

    1. <dependency>
    2. <groupId>com.mominggroupId>
    3. <artifactId>authority-spring-boot-starterartifactId>
    4. <version>14-SNAPSHOTversion>
    5. dependency>

    application.yml

    1. #配置authority-spring-boot-starter相关信息
    2. moming:
    3. authority:
    4. enable: true
    5. path-patterns: /api/**
    6. exclude-path-patterns: /api/user/*
    7. #设置token密钥
    8. token:
    9. secret: moming

    UserInfo

    1. @Data
    2. public class UserInfo {
    3. private Integer userId;
    4. private String tel;
    5. private String nickName;
    6. }

    UserInfoLocal

    1. public class UserInfoLocal {
    2. //线程独有变量
    3. private static ThreadLocal threadLocal = new InheritableThreadLocal<>();
    4. //给线程脑门上设置给变量
    5. public static void set(UserInfo userInfo){
    6. threadLocal.set(userInfo);
    7. }
    8. public static UserInfo get(){
    9. return threadLocal.get();
    10. }
    11. }

    AuthorityAutoConfiguration

    1. @ConditionalOnProperty(prefix = AuthorityAutoConfiguration.AUTHORITY_PRE,value = "enable", havingValue = "true", matchIfMissing = false)
    2. @Import(WebConfig.class)
    3. public class AuthorityAutoConfiguration {
    4. public final static String AUTHORITY_PRE="moming.authority";
    5. }

    AuthorityProperties

    1. @Data
    2. @ConfigurationProperties(prefix = AuthorityProperties.AUTHORITY_PRE)
    3. public class AuthorityProperties {
    4. public final static String AUTHORITY_PRE="moming.authority";
    5. /*
    6. 是否开启
    7. */
    8. private boolean enable ;
    9. /*
    10. 可以自定义配置拦截的路径
    11. */
    12. private String pathPatterns="/api/**";
    13. /*
    14. 配置不拦截的路径
    15. */
    16. private String excludePathPatterns="";
    17. }

    AuthorityInteceptor拦截器通过密钥验证请求头传过来的Authorization(token)是否合法

    1. public class AuthorityInteceptor implements HandlerInterceptor {
    2. //密钥
    3. private String TOKEN_SECRET;
    4. public AuthorityInteceptor(String tokenSecret){
    5. this.TOKEN_SECRET = tokenSecret;
    6. }
    7. @Override
    8. public boolean preHandle(HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception {
    9. String token = request.getHeader("Authorization");
    10. //token为空时
    11. if(StrUtil.isEmpty(token)){
    12. //response.sendError(response.SC_UNAUTHORIZED);
    13. response.setContentType("application/json;charset=utf-8");
    14. response.getWriter().write("{\"code\": 401,\"message\": \"token为空或已过期,请重新登录\"}");
    15. return false;
    16. }
    17. //验证token
    18. //将系统提示的异常抛出,使用自定义的提示信息message
    19. //校验是否正确(合法)
    20. boolean isTrue = false;
    21. try {
    22. //验证token,与密钥是否
    23. isTrue = JWTUtil.verify(token, TOKEN_SECRET.getBytes());
    24. } catch (Exception ex){
    25. ex.printStackTrace();
    26. }
    27. //验证不通过
    28. if (isTrue == false) {
    29. response.setContentType("application/json;charset=utf-8");
    30. response.getWriter().write("{\"code\":401,\"message\":\"您的token不正确或已过期\"}");
    31. return false;
    32. }
    33. //token验证通过解析token
    34. final JWT jwt = JWTUtil.parseToken(token);
    35. //System.out.println(jwt.getPayload().toString());
    36. //解析后获取PAYLOAD转换成UserInfo对象
    37. UserInfo userInfo = JSONUtil.toBean(jwt.getPayload().toString(), UserInfo.class);
    38. //给本地线程脑门上加个属性userInfo
    39. //目的是通过获取线程脑门上的属性userInfo来获取登录用户的信息
    40. UserInfoLocal.set(userInfo);
    41. //当前线程名称
    42. //System.out.println("111-"+Thread.currentThread().getName());
    43. return true;
    44. }
    45. }

    WebConfig

    1. @Import(AuthorityProperties.class)
    2. @Order(Ordered.HIGHEST_PRECEDENCE)
    3. public class WebConfig implements WebMvcConfigurer {
    4. @Autowired
    5. private AuthorityProperties authorityProperties;
    6. //密钥
    7. @Value("${moming.token.secret}")
    8. private String TOKEN_SECRET;
    9. @Override
    10. public void addInterceptors(InterceptorRegistry registry) {
    11. registry.addInterceptor(new AuthorityInteceptor(TOKEN_SECRET))
    12. .addPathPatterns(authorityProperties.getPathPatterns())
    13. .excludePathPatterns(authorityProperties.getExcludePathPatterns());
    14. }
    15. }

    通过对当前线程脑门上贴个东西,在controller中就可以获取到当前线程脑门上的东西

    如下

    1. //改
    2. @PutMapping
    3. public int update(@RequestBody ProductDto productDto){
    4. //获取当前线程脑门上的东西userInfo
    5. UserInfo userInfo = UserInfoLocal.get();
    6. //给更新人赋值
    7. productDto.setLastUpdateBy(userInfo.getNickName());
    8. return productService.update(productDto);
    9. }
    10. //插
    11. @PostMapping
    12. public int insert(@RequestBody ProductDto productDto){
    13. //获取线程脑门上的变量,给更新人赋值
    14. UserInfo userInfo = UserInfoLocal.get();
    15. productDto.setLastUpdateBy(userInfo.getNickName());
    16. return productService.insert(productDto);
    17. }

    这样在执行添加,编辑这些操作时就能获取当前登录用户的别名

    如下图所示

  • 相关阅读:
    I/O软件层次结构(用户层软件,设备独立性软件,设备驱动程序,中断处理程序,硬件)
    意大利航天飞行器公司【Sidereus】完成510万欧元融资
    新兴国家战略级安全话题-软件供应链安全
    10.DesignForSymbols\2.CompareLibraryTools...
    ​LeetCode解法汇总307. 区域和检索 - 数组可修改
    golang生成根证书,服务端证书,用于 tls
    论文解读(MLGCL)《Multi-Level Graph Contrastive Learning》
    【k8s】Pod 的钩子
    GUI编程--PyQt5--QAbstractButton
    免费分享一个springboot+vue校园宿舍管理系统,挺漂亮的
  • 原文地址:https://blog.csdn.net/HeyVIrBbox/article/details/127624401