spring-session应用配置及分布式应用session共享|同站点跨应用session共享
 2018-11-15 09:38:20   233   0   

本文最后更新于天前,文中介绍内容及环境可能已不适用.请谨慎参考.

公司系统慢慢的多起来了,需要一个集中统一的鉴权中心。

于是统一认证中心就顺势而生了。一套的,登陆认证,OAuth授权。

按部就班的概要设计,排开发计划,开干。

 

基本架构中需要一个前台用户中心,需要一个OAuth授权中心。

参考新浪openAPI,可以发现

前台用户中心为  open.sina.com

授权中心为 api.sina.com

 

即调用oauth认证的时候是走的api.sina.com,而这个是时候是需要取 open.sian.com的登陆信息的。

这里不可避免的需要面对一个问题,session的共享。

既然项目里面使用了spring,而且现在java项目基本没有不用spring的吧,好好研究了一番,记录分享备忘.

session原理

既然要session共享,基本的原理是不可不理解的.

简要原理

客户端访问页面的时候,

服务器会检查该请求头,cookie数据中是否包含JSESSIONID字段(jsessionid,phpsessionid等根据应用服务器不同而不用),

如果未包含,则服务器端应用会创造一个名为JSESSIONID(phpsessionid等根据应用服务器不同而不用)的输出 cookie返回给浏览器(A0)。默认情况应用服务器只会将session数据以HashTable的形式放入服务器内存(A1), 并不存在硬盘中。所以正常情况下服务器一重启,就需要重新登陆.

 

当已经包含sessionid时,服务端会检查找到与该session相匹配(A2)的信息,如果存在则直接使用该sessionid,若不存在则重新生成新的 session。

session的实现机制简要如下:

而这个sessionid基本上是通过服务器返回 set-cookie带到客户端,然后客户端通过cookie来存储sessionid等信息.

下次客户端请求时,会读取本地的cookie,将与当前请求页面路径匹配的sessionid发送至服务器端来保持会话.

session基本上是通过cookie来实现的.

 

如下,第一次访问127.0.0.1:8087/account-web-oauth-test/时,服务器返回的 Set-cookie,带回了sessionid即path.

 

参数domain,path

而服务器返回时的

Set-Cookie 字段的属性中有两个比较关键的属性,前文A0用于客户端存储,然后当客户端再次请求时用于A2过程的比较

属性 说明
path=PATH 将服务器上的文件目录作为 Cookie 的适用对象(即访问指定path即改path的子路径时才能访问此cookie)
domain=域名 作为 Cookie 适用对象的域名(即访问指定域名下的路径时才能访问此cookie)

 

默认情况下

path会与你访问的地址相同,比如 前台应用  127.0.0.1:8081/front/test.action   就是  /front

比如 前台应用  127.0.0.1:8082/oauth/oauthorize.action   就是  /oauth

而这两个cookie数据是不能共享的, 共享的只能是子目录.

比如: 访问 127.0.0.1:8081/front/xx/xx.action时是可以使用/front下的cookie数据的

 

domian也是一一样,只是domain不会区分端口。

127.0.0.1:8082 与127.0.0.1:8083 是一样的 127.0.0.1

而 api.sina.com 与open.sina.com 时不一样的.

 

session共享

session共享即需要解决上面提到的

A0,

A1,

A2 

3个问题即可.

 

A1:通过spring-session将session数据不再保存在内存,而是保持到redis数据库中,所有的不同机器上的分布式应用或者同一个机器上的不同应用都访问同一个redis即可.

A0,A2可以一并解决。通过spring-session配置.

 

 

spring-session

使用spring-session模块来保存session,将各种应用服务器默认保存在内存的session信息存储到redis数据库库中。

来达到共享的目的。

这样一来,即使是web应用重启了也不会影响登陆状态。

pom.xml配置

使用redis集群配置,redis是内存数据库,机器一旦挂掉数据就全没了,还是集群保险点吧。

       <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session-data-redis</artifactId>
                <version>1.3.1.RELEASE</version>
                <type>pom</type>
        </dependency>
        
           <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
	

 

web.xml配置

配置过滤器,处理session。


  <!-- spring-session config -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:spring-session.xml</param-value>
    </context-param>

    <!-- 这个filter 要放在第一个  与shiro公用的时候,shiro的fitler需要放到下面 -->
    <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>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

 

这里有个问题,与shiro一同使用的时候,shiro的filter也是 DelegatingFilterProxy

与上面是一样的,但是上面那个spring-session的必须放到前面!

<!-- 配置shiro -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 

 

spring-session.xml

配置session bean

	<!-- RedisHttpSessionConfiguration -->
	<bean id="redisHttpSessionConfiguration"
	class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
		<property name="maxInactiveIntervalInSeconds"
			value="${redis.timeout}" />    <!-- session过期时间,单位是秒 -->
	</bean>

<!--JedisConnectionFactory -->
	<bean id="jedisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
		<constructor-arg name="clusterConfig"
			ref="redisClusterConfig" />
		<property name="timeout" value="${redis.timeout}" />
		<property name="poolConfig" ref="jedisPoolConfig" />

		<property name="password" value="${redis.master.passwd}" />
	</bean>


	<!-- 加载properties文件 -->
	<bean id="configProperties"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:config/redis.properties</value>
			</list>
		</property>
	</bean>

<!-- Redis集群配置 -->
	<bean id="redisClusterConfig"
		class="org.springframework.data.redis.connection.RedisClusterConfiguration">
		<property name="maxRedirects" value="6"></property>
		<property name="clusterNodes">
			<set>
				<bean
					class="org.springframework.data.redis.connection.RedisNode">
					
					<constructor-arg name="host"
						value="${redis.node1.host}" />
					<constructor-arg name="port"
						value="${redis.node1.port}" />

					
				</bean>

				<bean
					class="org.springframework.data.redis.connection.RedisNode">
					<constructor-arg name="host"
						value="${redis.node2.host}" />
					<constructor-arg name="port"
						value="${redis.node2.port}" />
				</bean>

				<bean
					class="org.springframework.data.redis.connection.RedisNode">
					<constructor-arg name="host"
						value="${redis.node3.host}" />
					<constructor-arg name="port"
						value="${redis.node3.port}" />
				</bean>
				
			</set>
		</property>
	</bean>

 

A0,A2-domain/path问题

 

参见https://docs.spring.io/spring-session/docs/1.3.3.RELEASE/reference/html5/guides/custom-cookie.html#custom-cookie-spring-configuration

通过配置spring-session.xml

<bean id="cookieSerializer"
		class="org.springframework.session.web.http.DefaultCookieSerializer">
		<property name="cookieName" value="ASESSIONID" />
		<property name="cookiePath" value="/" />
		<property name="domainNamePattern" value="^.+?\.(\w+\.[a-z]+)$"></property>
	</bean>

将cookie Path都设置为 /

而cookie domain进行正则匹配, 过滤第二个点后面的部分,

即 api.sina.com --> sina.com

     open.sina.com--> sina.com 

这样session即可跨域,跨应用共享

其他问题

!2018-11-27!

另外之前本来已经调好的共享,昨天重新启动项目竟然各种不共享,搞了一天没弄出来,

今天再来,一下就好了。。我了个去。浪费一天。

最后一个一个配置的确认,发现是浏览器session的问题,之前开发,本地缓存了各种session数据。

而每次清理浏览器数据的时候都只是清理缓存,而没有清理session.当然平常这样是没有问题,但是对于调试session共享来说,这就是致命的!

所有,有同样问题的,或者调试时spring-session不对,再确认配置没有问题的情况下,可以先尝试清除浏览器session缓存数据

上面我把spring-session的名字改成了ASESSION就是这个目的,需要确认spring-session有没起作用。

同时可以通过浏览器的调试工具查看是否起作用了

不同应用,cookie数据字段及值时一致的

 

说明二(shiro直接实现session存储至redis)

当然昨天一天实际上也是有点收获。

了解另外一种方式,实际也也是可以不用spring-session,而直接重载shiro里面的sessionmanager来实现一样的功能。

但是,

本文说明的方法,spring-session是一个独立的,可以与shiro互不影响,个人觉得这样使用,适应性/可移植性更强一点,当然,

仁者见仁智者见智~。

 

结果

账号 测试喵 登陆应用中心   127.0.0.1:8085/account-web-front 应用

在测试应用 127.0.0.1:8087/account-web-oauth-test  独立测试应用 中oauth登陆 

 

直接跳转 127.0.0.1:8086/account-web-oauth   独立oauth授权中心应用。通过共享session获取 应用中心共享的登陆信息.

登陆完成: 

 127.0.0.1:8087/account-web-oauth-test  独立应用.


 2018-12-18 16:55:21 
 0



发表新的评论
{{s_uid}}   , 欢迎回来.
您的称呼(*必填):
您的邮箱地址(*必填,您的邮箱地址不会公开,仅作为有回复后的消息通知手段):
您的站点地址(选填):
留言:

∑( ° △ °|||)︴

(๑•̀ㅂ•́)و✧
<( ̄) ̄)>
[]~( ̄▽ ̄)~*
( ̄ˇ ̄)
[]~( ̄▽ ̄)~*
( ̄ˇ ̄)
╮( ̄▽ ̄)╭
( ̄ε(# ̄)
(⊙ˍ⊙)
( ̄▽ ̄)~*
∑( ° △ °|||)︴

文章分类

可能喜欢 

KxのBook@Copyright 2017- All Rights Reserved
Designed and themed by 野生的喵喵   1621353   44910