RedisSession (自定义)

RedisSession (自定义)

疯狂创客圈 Java 高并发【 亿级流量聊天室实战】实战系列 【博客园总入口

架构师成长+面试必备之 高并发基础书籍 【Netty Zookeeper Redis 高并发实战


前言

Crazy-SpringCloud 微服务脚手架 &视频介绍

Crazy-SpringCloud 微服务脚手架,是为 Java 微服务开发 入门者 准备的 学习和开发脚手架。并配有一系列的使用教程和视频,大致如下:

高并发 环境搭建 图文教程和演示视频,陆续上线:

中间件 链接地址
Linux Redis 安装(带视频) Linux Redis 安装(带视频)
Linux Zookeeper 安装(带视频) Linux Zookeeper 安装, 带视频
Windows Redis 安装(带视频) Windows Redis 安装(带视频)
RabbitMQ 离线安装(带视频) RabbitMQ 离线安装(带视频)
ElasticSearch 安装, 带视频 ElasticSearch 安装, 带视频
Nacos 安装(带视频) Nacos 安装(带视频)

Crazy-SpringCloud 微服务脚手架 图文教程和演示视频,陆续上线:

组件 链接地址
Eureka Eureka 入门,带视频
SpringCloud Config springcloud Config 入门,带视频
spring security spring security 原理+实战
Spring Session SpringSession 独立使用
分布式 session 基础 RedisSession (自定义)
重点: springcloud 开发脚手架 springcloud 开发脚手架
SpingSecurity + SpringSession 死磕 (写作中) SpingSecurity + SpringSession 死磕

小视频以及所需工具的百度网盘链接,请参见 疯狂创客圈 高并发社群 博客

RedisSession 场景和问题

一般,大家获取 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 高并发实战

img


疯狂创客圈 Java 死磕系列

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

原文地址:https://www.cnblogs.com/crazymakercircle/p/12038208.html