Spring Session 實現單點登錄
此種方式相對於上節(https://blog.csdn.net/sinat_25295611/article/details/80406172)所說使用原生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 引入依賴
<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 在Spring的配置文件中添加配置
<bean id="redisHttpSessionConfiguration"
class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<!-- session 過期時間默認爲30分鐘-->
<property name="maxInactiveIntervalInSeconds" value="1800"/>
</bean>
<!--TODO 自定義寫入的Cookie設置 -->
<!--<bean id="defaultCookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer">
<property name="cookieName" value="SESSION_NAME"/>
<property name="cookiePath" value="/"/>
<property name="domainName" value=".kaymmall.com"/>
<property name="useHttpOnlyCookie" value="true"/>
<property name="cookieMaxAge" value="3600*24*365"/>
</bean>-->
<!-- 連接池配置,這裏使用Jedis的連接池-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="20"/>
</bean>
<!-- jedisConnectionFactory-->
<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>
5 小結
通過Redis+Spring Session實現的單點登錄系統對業務代碼侵入性非常小,在單服務器時使用session存取用戶信息,在集羣模式時代碼無需改變,只要引入Spring Session相關配置即可。