RedisSession (自定义)
疯狂创客圈 Java 高并发【 亿级流量聊天室实战】实战系列 【博客园总入口 】
架构师成长+面试必备之 高并发基础书籍 【Netty Zookeeper Redis 高并发实战 】
疯狂创客圈 高并发 环境 视频,陆续上线:
- Windows Redis 安装(带视频)
- Linux Redis 安装(带视频)
- Windows Zookeeper 安装(带视频)
- Linux Zookeeper 安装(带视频)
- RabbitMQ 离线安装(带视频)
- Nacos 安装(带视频)
- ElasticSearch 安装, 带视频**
小视频以及所需工具的百度网盘链接,请参见 疯狂创客圈 高并发社群 博客
场景和问题
一般,大家获取 Session 的方式: session = request.getSession(), 是通过HttpServletRequest 获取的,因为每次用户请求过来,我们服务端都会获取到请求携带的唯一 SessionId。
如果自定的 HttpSession的,所以我们还要自定义一个 HttpServletRequest 的包装类,使得每次请求获取的都是我们自己的HttpSession。
还有一点 ,如何 使用HttpServletRequest 包装类呢?
还需要自定义一个 Filter,这个Filter不干其它的事情,就负责把HttpServletRquest 换成我们自定义的包装类。
第一步 ,定义一个 RedisHttpSession
RedisHttpSession 实现 HttpSession 接口 ,选择Redis存储属性,达到分布式的目标。
session在 Redis中 选择的 Hash 结构存储,以 sessionId 作为Key,有些方法不需要实现。
//首先我说过,HttpSession是不能注入属性的,所以就需要依赖 上面定义的那个 工具类,获取bean //如果不加此注解,你的属性就会为空,获取不到 @DependsOn("applicationContextUtil") @SpringBootConfiguration public class CustomRedisHttpSession implements HttpSession { private HttpServletRequest httpServletRequest; private HttpServletResponse httpServletResponse; private Cookie[] cookies; //sessionId private String sessionId; public CustomRedisHttpSession(){} public CustomRedisHttpSession(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,String sid){ this.httpServletRequest = httpServletRequest; this.httpServletResponse = httpServletResponse; this.cookies = cookies; this.sessionId =sid; } /** * 获取指定属性值 * @param key 属性key * @return key对应的value */ @Override public Object getAttribute(String key) { if(sessionId != null){ return sessionRedisTemplate.opsForHash().get(sessionId,key); } return null; } /** * 之前说过了,此类属性不能注入,只能通过手动获取 */ @SuppressWarnings("unchecked") private final RedisTemplate<String,Object> sessionRedisTemplate = =ApplicationContextUtil.getBean("sessionRedisTemplate",RedisTemplate.class); //sessionId 的前缀 private static final String SESSIONID_PRIFIX="yangxiaoguang"; /** * 设置属性值 * @param key key * @param value value */ @Override public void setAttribute(String key, Object value) { if (sessionId != null) { sessionRedisTemplate.opsForHash().put(sessionId, key, value); }else{ //如果是第一次登录,那么生成 sessionId,将属性值存入redis,设置过期时间,并设置浏览器cookie this.sessionId = SESSIONID_PRIFIX + UUID.randomUUID(); setCookieSessionId(sessionId); sessionRedisTemplate.opsForHash().put(sessionId, key, value); sessionRedisTemplate.expire(sessionId, sessionTimeout, TimeUnit.SECONDS); } } //session的过期时间,8小时 private final int sessionTimeout=28800; //将sessionId存入浏览器 private void setCookieSessionId(String sessionId){ Cookie cookie = new Cookie(SESSIONID,sessionId); cookie.setPath("/"); cookie.setMaxAge(sessionTimeout); this.httpServletResponse.addCookie(cookie); } /** * 移除指定的属性 * @param key 属性 key */ @Override public void removeAttribute(String key) { if(sessionId != null){ sessionRedisTemplate.opsForHash().delete(sessionId,key); } } }
第2步 ,定义一个 ServletRequestWrapper
如果自定的 HttpSession的,所以我们还要自定义一个 HttpServletRequest 的包装类,使得每次请求获取的都是我们自己的HttpSession。
public class CustomSessionHttpServletRequestWrapper extends HttpServletRequestWrapper{ private HttpServletRequest httpServletRequest; private HttpServletResponse httpServletResponse; //自定义Session private CustomRedisHttpSession customRedisHttpSession; public CustomSessionHttpServletRequestWrapper(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){ super(httpServletRequest); this.httpServletRequest = httpServletRequest; this.httpServletResponse = httpServletResponse; Cookie[] cookies = httpServletRequest.getCookies(); String sid= getCookieSessionId(cookies); this.customRedisHttpSession = new CustomRedisHttpSession(httpServletRequest,httpServletResponse,sid); } //这个方法就是最重要的,通过它获取自定义的 HttpSession @Override public HttpSession getSession() { return this.customRedisHttpSession; } //浏览器的cookie key private static final String SESSIONID="xyzlycimanage"; //从浏览器获取SessionId private String getCookieSessionId(Cookie[] cookies){ if(cookies != null){ for(Cookie cookie : cookies){ if(SESSIONID.equals(cookie.getName())){ return cookie.getValue(); } } } return null; } }
如果从header 中获取 sessiondi,也是类似的:
public class CustomSessionHttpServletRequestWrapper extends HttpServletRequestWrapper{ private HttpServletRequest httpServletRequest; private HttpServletResponse httpServletResponse; //自定义Session private CustomRedisHttpSession customRedisHttpSession; public CustomSessionHttpServletRequestWrapper(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){ super(httpServletRequest); this.httpServletRequest = httpServletRequest; this.httpServletResponse = httpServletResponse; String sid= request.getHeader("SESSION_ID"); this.customRedisHttpSession = new CustomRedisHttpSession(httpServletRequest,httpServletResponse,sid); } //这个方法就是最重要的,通过它获取自定义的 HttpSession @Override public HttpSession getSession() { return this.customRedisHttpSession; } //浏览器的cookie key private static final String SESSIONID="xyzlycimanage"; //从浏览器获取SessionId private String getCookieSessionId(Cookie[] cookies){ if(cookies != null){ for(Cookie cookie : cookies){ if(SESSIONID.equals(cookie.getName())){ return cookie.getValue(); } } } return null; } }
第三步: 定义一个 Filter 类
还有一点 ,如何 使用HttpServletRequest 包装类呢?
还需要自定义一个 Filter,这个Filter不干其它的事情,就负责把HttpServletRquest 换成我们自定义的包装类。
/** * 此过滤器拦截所有请求,也放行所有请求,但是只要与Session操作的有关的请求都换被 * 替换成:CustomSessionHttpServletRequestWrapper包装请求, * 这个请求会获取自定义的HttpSession */ public class CustomSessionRequestFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //将请求替换成自定义的 CustomSessionHttpServletRequestWrapper 包装请求 HttpServletRequest customSessionHttpServletRequestWrapper = new CustomSessionHttpServletRequestWrapper ((HttpServletRequest)servletRequest,(HttpServletResponse)servletResponse); filterChain.doFilter(customSessionHttpServletRequestWrapper, httpServletResponse); //替换了 请求哦 filterChain.doFilter(customSessionHttpServletRequestWrapper,servletResponse); } /** * init 和 destroy 是管理 Filter的生命周期的,与逻辑无关,所以无需实现 */ @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void destroy() {} }
四步 装载 Filter 类
可以独立加载,也可以放在springsecurity 的配置类中。
如果放在springsecurity 的配置类,具体如下:
package com.gsafety.pushserver.message.config; //... @EnableWebSecurity() public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers( //... "/actuator/hystrix", "/actuator/hystrix.stream", "/v2/api-docs", "/swagger-resources/configuration/ui", "/swagger-resources", "/swagger-resources/configuration/security", "/swagger-ui.html") .permitAll() // .antMatchers("/image/**").permitAll() // .antMatchers("/admin/**").hasAnyRole("ADMIN") .and() .authorizeRequests().anyRequest().authenticated() .and() .formLogin().disable() .sessionManagement().disable() .and() .logout().disable() .addFilterBefore(new CustomSessionRequestFilter(), SessionManagementFilter.class) .sessionManagement().disable(); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers( //.... "/actuator/hystrix.stream", "/actuator/hystrix", "/api/mock/**", "/v2/api-docs", "/swagger-resources/configuration/ui", "/swagger-resources", "/swagger-resources/configuration/security", "/api/gemp/duty/info/user/login", "/swagger-ui.html", "/css/**", "/js/**", "/images/**", "/webjars/**", "**/favicon.ico" ); } }
具体,请关注 Java 高并发研习社群 【博客园 总入口 】
最后,介绍一下疯狂创客圈:疯狂创客圈,一个Java 高并发研习社群 【博客园 总入口 】
疯狂创客圈,倾力推出:面试必备 + 面试必备 + 面试必备 的基础原理+实战 书籍 《Netty Zookeeper Redis 高并发实战》
疯狂创客圈 Java 死磕系列
Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战
- Netty 源码、原理、JAVA NIO 原理
- Java 面试题 一网打尽
疯狂创客圈 【 博客园 总入口 】
来源:https://www.cnblogs.com/crazymakercircle/p/12038208.html