用户登录信息保存在ThreadLocal中

说明 : 简单说下我写这篇文章的来由吧,最近准备新开一个项目前后端分离的,后端通过springboot实现,前段就不说了,那么就会考虑到用户登录成功以后登录信息保存在什么地方,是通过前后端一直传递参数么,那就蛋疼了。通过session存储信息。。那不是要每次都要在视图层获取session在一层一层的传递下去。。想了想通过ThreadLocal来存储用户信息吧,这样就可以直接在dao层调用了。说干就干、走起。

1 建立ThreadLocal实体类

package com.fy.agent.api.util.common;

import com.fy.agent.api.entity.vo.mongo.ConsoleUserVo;

/**
 * @author : lqf
 * @description : 用于存储用户信息
 * @date : Create in 10:47 2018/6/04
 */
public class AgentThreadLocal {

    private AgentThreadLocal(){
    }
    //ConsoleUserVo是存储用户信息的实体类我就不说了
    private static final ThreadLocal<ConsoleUserVo> LOCAL = new ThreadLocal<ConsoleUserVo>();

    public static void set(ConsoleUserVo user){
        LOCAL.set(user);
    }

    public static ConsoleUserVo get(){
        return LOCAL.get();
    }

    public static void remove(){
        LOCAL.remove();
    }
}

由于每一次请求都是一个独立的线程,ThreadLocal中的变量需要我们通过session做一个中专的配置,每次请求都判断这个session中是否存在用户信息,如果session中存在用户信息就将用户信息保存到ThreadLocal中,下面上代码
2 创建sessionFilter

package com.fy.agent.api.config.filter;


import com.fy.agent.api.entity.vo.mongo.ConsoleUserVo;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author : lqf
 * @description :
 * @date : Create in 13:12 2018/6/26
 */
public class SessionFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        ConsoleUserVo userSession = (ConsoleUserVo)request.getSession().getAttribute("userInfo");
        if (userSession != null) {
            //先销毁在添加否则触发不了监听器中的attributeAdded
            request.getSession().removeAttribute("userInfo");
            //重新设值session
            request.getSession().setAttribute("userInfo", userSession);
        }
        chain.doFilter(req, res);
    }

    @Override
    public void destroy() {

    }
}

上面代码中的销毁session是个坑,经测试如果session的key相同value也相同这个时候重新给session赋值是不会触发session监听器的创建和替换方法的,下面看下session监听

package com.fy.agent.api.config.filter;

import com.fy.agent.api.entity.vo.mongo.ConsoleUserVo;
import com.fy.agent.api.util.common.AgentThreadLocal;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

/**
 * @author : lqf
 * @description :
 * @date : Create in 15:17 2018/6/26
 */
@WebListener
public class SessionAttributeListener implements HttpSessionAttributeListener {
    @Override
    //创建session时触发
    public void attributeAdded(HttpSessionBindingEvent event) {
        if ("userInfo".equals(event.getName())) {
            AgentThreadLocal.set((ConsoleUserVo) event.getValue());
        }
    }

    @Override
    //销毁session时触发
    public void attributeRemoved(HttpSessionBindingEvent event) {
        if ("userInfo".equals(event.getName())) {
            AgentThreadLocal.remove();
        }
    }

    @Override
    //替换session时触发
    public void attributeReplaced(HttpSessionBindingEvent event) {
        if ("userInfo".equals(event.getName())) {
            AgentThreadLocal.set((ConsoleUserVo) event.getValue());
        }
    }
}

springboot启动类中创建filter实例

package com.fy.agent.api;

import com.fy.agent.api.config.filter.HttpBearerAuthorizeAttribute;
import com.fy.agent.api.config.filter.SessionFilter;
import com.fy.agent.api.entity.common.Audience;
import com.google.common.hash.Hashing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;

@ServletComponentScan
@SpringBootApplication
public class AgentApiApplication {
    private static Logger LOG = LoggerFactory.getLogger(AgentApiApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(AgentApiApplication.class, args);
    }

    @Bean
    public FilterRegistrationBean jwtFilterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        HttpBearerAuthorizeAttribute httpBearerFilter = new HttpBearerAuthorizeAttribute();
        registrationBean.setFilter(httpBearerFilter);
        registrationBean.setOrder(Integer.MAX_VALUE);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean sessionFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        SessionFilter sessionFilter = new SessionFilter();
        registrationBean.setFilter(sessionFilter);
        registrationBean.setOrder(Integer.MIN_VALUE);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }
}

这里需要注意的是启动类上需要加上@ServletComponentScan注解,你们可能会说你的启动类中为什么两个filter啊,这是我特意加上的,在我们的项目实战中可能不只一个filter有可能是3个5个的对吧,这个时候我们就要定义这些filter的执行顺序了对吧。可能有的人会说博主你这有些麻烦啊!直接通过注解来实现呗@Order(Integer.MAX_VALUE) //执行排序值越小越先执行
@WebFilter(filterName = “sessionFilter”, urlPatterns = “/*”)
这里有个坑我现在没有找到他的错误原因,我使用的springboot1.5通过注解定义的排序顺序不生效,通过手动常见的bean是能正常生效的。你们如果找到这个问题共享一下啊,谢了

回归正题

//登录成功
 ConsoleUserVo userVo = new ConsoleUserVo();
 userVo.setUserName("");
 userVo.setAge("");
 //将用户信息存储在session中这个时候就会调用session监听器的创建方法
 request.getSession().setAttribute("userInfo", userVo);
 //第二遍给已经存储的key付相同的值是不能调用session监听中的任何方法的
 request.getSession().setAttribute("userInfo", userVo);
 //第三遍存储session key是已经存在的但是值变了,这个时候是可以触发监听的替换方法的
 request.getSession().setAttribute("userInfo", "123123");

上面的session存储纯粹是为了告诉大家什么时候触发监听,正常逻辑中只写一个存储就可以了,通过这个存储触发大家就能明白上面的sessionFilter中为什么要删除session,在进行存储了。

到这里大家就可以再任一方法中进行
AgentThreadLocal .get()方法了。

欢迎大家指正沟通

  • 6
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
用户信息保存ThreadLocal是通过将用户信息存储ThreadLocal的value实现的。具体来说,我们可以使用一个自定义的ThreadLocal类,在其设置put()方法将用户信息存储ThreadLocal的value,使用get()方法获取用户信息,使用remove()方法在使用完用户信息后将其从ThreadLocal删除,以避免内存泄漏的风险。例如,在一个名为UserThreadLocal的类,我们可以定义put()、get()和remove()方法来实现用户信息保存和获取。通过调用put()方法将用户信息存储ThreadLocal,然后在需要使用用户信息的地方调用get()方法获取用户信息。最后,在合适的机,例如在登录拦截器的afterCompletion()方法,使用remove()方法将ThreadLocal用户信息删除,以免造成内存泄漏。这样可以保证在同一个线程,多个地方都能够方便地获取到相同的用户信息,而不会相互干扰。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ThreadLocal存储用户登录信息](https://blog.csdn.net/weixin_46000937/article/details/126828778)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [ThreadLocal实现登录保存用户登录信息)](https://blog.csdn.net/qq_56851614/article/details/125464270)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值