说明 : 简单说下我写这篇文章的来由吧,最近准备新开一个项目前后端分离的,后端通过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()方法了。
欢迎大家指正沟通