RedisSession (自定义)

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 高并发实战

RedisSession  (自定义)


疯狂创客圈 Java 死磕系列

  • Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战

  • Netty 源码、原理、JAVA NIO 原理
  • Java 面试题 一网打尽
  • 疯狂创客圈 【 博客园 总入口 】


上一篇:filter实现拦截没有登录的用户


下一篇:配置简单的拦截器java中