本篇将介绍如何实现同域集群之间共享session,以及通过cookie跨域实现不同二级域名共享session。
spring session
session通常是保存在服务器内存中的,而在集群环境下,用户每次请求会被发送到不同服务器,并且分别操作session,会造成session数据不一致。
spring session默认会将session保存到redis中。不管有多少服务器,都通过redis读写session,实现session数据一致。
在spring boot环境下,添加以下依赖就会自动连接到本机redis,自动实现session共享。需要自行装好redis运行。
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
cookie和session
当用户输入账号/密码登录时,服务器会建立session来维持用户状态,并且生成一个不会重复的字符串作为sessionId,将它传给浏览器,浏览器在之后的请求中,都会在cookie中附带sessionId,服务器由此确认当前用户的session。在PC浏览器上,这个过程是完全自动的,并不需要开发人员参与。而在手机端,如APP、小程序等并不支持,需要后端开发人员手动获取到sessionId,前端开发人员手动将sessionId添加到请求头的cookie中,可实现和PC浏览器一样的效果。
我们知道,前端 js 可以获取和擅改cookie,这是否存在安全问题?在spring security中,有一系列安全机制,会自动解决这些问题,这里不多说了。
跨域
上面是同一项目集群,是同域环境。如果有2个不同的项目,如a.xxx.com和b.xxx.com,则出现跨域。即使是同一个浏览器的用户请求,也会产生2个不同的session,因为session只在当前域范围内有效。
实现同域
如果想让a.xxx.com和b.xxx.com共享session,需要配置cookie同域
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID"); //1
serializer.setDomainName("xxx.com"); //2
serializer.setCookiePath("/"); //3
return serializer;
}
- spring session默认使用的cookie名是SESSIONID,而传统的java web服务器中名字是JSESSIONID,开头的J代表java,建议和传统保持一致,用途都是一样的。
- a.xxx.com和b.xxx.com都是二级域名,可以将项目的cookie域设置为公共的顶级域名xxx.com,从而实现同域。
- 在一个tomcat中,可以同时部署多个项目,每个项目的path不一样,导致cookiePath不一致也算跨域,所以这里设置统一的 / 根path。
跨域实践
google chrome浏览器,不配置同域,请求dev.xxx.com 和test.xxx.com,依次点击红框,可以查看到cookie是不一样,域名也不一样,而且cookie名是SESSION
同域实践
配置同域以后,请求dev.xxx.com 和test.xxx.com,SESSION变成了JSESSION,域名变成了相同的xxx.com,并且cookie内容是一模一样的。