1. 程式人生 > >Spring Session+Redis零侵入實現單點登入

Spring Session+Redis零侵入實現單點登入

Spring Session 實現單點登入

此種方式相對於上節所說使用原生(Jedis+Jackson+Cookie+Filter)的方式實現起來更加簡便,同時對業務程式碼的侵入性也十分之小,其原理與原生方式類似,並通過對HttpServletRequest和HttpServletResponse的包裝來實現cookie的讀寫,序列化採用JDK原生的方式,故使用者物件(User)需要實現Serializable介面, 
在說明具體如何配置之前,有必要說一下,在本專案中分散式Session是如何演進的。

在最初,我們的User模組是這樣的,使用者登入資訊存入Tomcat容器自帶的Session中,這也是通用的做法,也很簡單,適合單伺服器部署:

v1.0 版本

/**
     *使用者登入
     */
    @RequestMapping(value = "login.do",method = RequestMethod.GET)
    @ResponseBody
    public ServerResponse<User> login(String username, String password, HttpSession session){

        ServerResponse<User> response = iUserService.login(username, password);
        if (response.isSuccess()) {
            session.setAttribute(Const.CURRENT_USER,response.getData());
        }
        return response;
    }

v2.0版本

此時隨著系統演進,伺服器升級為叢集模式,或多子系統等架構,Tomcat自帶的Session管理是基於Http協議的無狀態物件,故我們將Session集中管理起來,放入第三方,比如Redis快取中,所有服務節點重Session會話中心進行登入認證。單點登入的實現方式為 Session存入Redis,token寫入Cookie,使用者帶著Cookie中的token來進行認證,此時,我們使用原生方式實現,程式碼會是這樣:

/**
     *使用者登入
     */
    @RequestMapping(value = "login.do",method = RequestMethod.GET)
    @ResponseBody
    public ServerResponse<User> login(String username, String password, HttpSession session,HttpServletResponse httpServletResponse){

        ServerResponse<User> response = iUserService.login(username, password);
        if (response.isSuccess()) {
//            session.setAttribute(Const.CURRENT_USER,response.getData());
            //寫入cookie
            CookieUtil.writeLoginToken(httpServletResponse,session.getId());
            //將登入使用者資訊存入redis,有效時間為30分鐘
            RedisPoolUtil.setEx(session.getId(), JsonUtil.obj2string(response.getData()), Const.RedisCacheExTime.REDIS_SESSION_EXTIME);
        }
        return response;
    }

v3.0 從上面的實現方式也看出來了,在login方法中加入了讀寫cookie和redis的邏輯,在我們的業務中通常會有很多地方需要對使用者資訊進行認證,故這樣的程式碼對業務程式碼的侵入性很大,很多地方都要寫這些業務無關程式碼。

這時我們使用Spring Session提供的方式來實現分散式的Session管理,其原理是與V2.0一樣的,不過Spring對其進行的封裝和優化。整合Spring Session之後,我們的程式碼於是就又變回了V1.0的寫法:

這樣的好處就是,在系統演進的過程中,我們不需要改變原有程式碼,迭代的複雜度也大大降低。

 @RequestMapping(value = "login.do",method = RequestMethod.GET)
    @ResponseBody
    public ServerResponse<User> login(String username, String password, HttpSession session){

        ServerResponse<User> response = iUserService.login(username, password);
        if (response.isSuccess()) {
            session.setAttribute(Const.CURRENT_USER,response.getData());
        }
        return response;
    }

Spring Session配置

1 引入依賴,並將Spring版本改成4.0.3

<dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
      <version>1.3.1.RELEASE</version>
</dependency>

2 在web.xml中加入過濾器DelegatingFilterProxy

<!-- spring session 的過濾器-->
    <filter>
        <filter-name>springSessionRepositoryFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSessionRepositoryFilter</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>

3 在applicationContext.xml中<import resource="applicationContext-spring-session.xml"/>
   並在applicationContext-spring-session的配置檔案中新增配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">


    <bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
        <property name="maxInactiveIntervalInSeconds" value="1800" />
    </bean>

    <bean id="defaultCookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer">
        <property name="domainName" value=".happymmall.com" />
        <property name="useHttpOnlyCookie" value="true" />
        <property name="cookiePath" value="/" />
        <property name="cookieMaxAge" value="31536000" />
    </bean>

    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="20"/>
    </bean>

    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="127.0.0.1" />
        <property name="port" value="6379" />
        <property name="poolConfig" ref="jedisPoolConfig" />
    </bean>

</beans>

5 小結

通過Redis+Spring Session實現的單點登入系統對業務程式碼侵入性非常小,在單伺服器時使用session存取使用者資訊,在叢集模式時程式碼無需改變,只要引入Spring Session相關配置即可。