RedisSession (自定义)

疯狂创客圈 《SpringCloud Nginx 高并发核心编程》高并发 架构师 必备【链接

在这里插入图片描述


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) 聊天程序【 亿级流量】实战 开源项目实战

posted @ 2019-12-14 09:46  疯狂创客圈  阅读(1995)  评论(0编辑  收藏  举报