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) 聊天程序【 億級流量】實戰 開源項目實戰


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM