作者:没搜摸索摸索_685 | 来源:互联网 | 2023-09-25 19:01
官方文档地址:http:projects.spring.iospring-sessionhttp:docs.spring.iospring-sessiondocscurrentre
官方文档地址:http://projects.spring.io/spring-session/
http://docs.spring.io/spring-session/docs/current/reference/html5/guides/httpsession.html
Spring Session为企业级Java应用的session管理带来了革新,使得以下的功能更加容易实现:
- 将session所保存的状态卸载到特定的外部session存储中,如Redis、mongo或gemfire中,它们能够以独立于应用服务器的方式提供高质量的集群。
- 当用户使用WebSocket发送请求的时候,能够保持HttpSession处于活跃状态。
- 在非Web请求的处理代码中,能够访问session数据,比如在JMS消息的处理代码中。
- 支持每个浏览器上使用多个session,从而能够很容易地构建更加丰富的终端用户体验。
- 控制session id如何在客户端和服务器之间进行交换,这样的话就能很容易地编写Restful API,因为它可以从HTTP 头信息(或者参数)中获取session id,而不必再依赖于COOKIE。
话不多说,上集成代码吧(*^__^*) …
首先加入maven引用:
<dependency>
<groupId>org.springframework.sessiongroupId>
<artifactId>spring-session-data-redisartifactId>
<version>1.2.2.RELEASEversion>
<type>pomtype>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>4.2.5.RELEASEversion>
dependency>
HttpSessionConfig:
//maxInactiveIntervalInSeconds为session过期时间,这里注意session过期时间配置在web.xml里面是不起作用的
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=999)
public class HttpSessionConfig {
//这里有个小坑,如果服务器用的是云服务器,不加这个会报错
@Bean
public static ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
//这里是reids连接配置
@Bean
public JedisConnectionFactory connectionFactory() {
JedisConnectionFactory connection = new JedisConnectionFactory();
connection.setPort(6379);
connection.setHostName("127.0.0.1");
connection.setPassword("ps123456");
connection.setDatabase(8);
return connection;
}
//session策略,这里配置的是Header方式(有提供Header,COOKIE等方式),可自定义,后面会详细讲
@Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy();
}
}
Initializer:
public class Initializer extends AbstractHttpSessionApplicationInitializer {
}
到这里就可以把工程跑起来了,上面提到使用的session策略是HeaderHttpSessionStrategy,restful可以使用这种方式。看HeaderHttpSessionStrategy源码可以知道,HeaderHttpSessionStrategy实现了HttpSessionStrategy接口。哈~也就是说,如果要自定义策略的话,也实现HttpSessionStrategy就可以了。
getRequestedSessionId()方法,获取客户端传过来的sessionid,如果没有传spring会通过UUID的方式分配一个(这里springsession没有支持自定义sessionid
),
然后onNewSession()和onInvalidateSession()就不用说了,创建和销毁嘛~
public class HeaderHttpSessionStrategy implements HttpSessionStrategy {
private String headerName = "x-auth-token";
public String getRequestedSessionId(HttpServletRequest request) {
return request.getHeader(this.headerName);
}
public void onNewSession(Session session, HttpServletRequest request,
HttpServletResponse response) {
response.setHeader(this.headerName, session.getId());
}
public void onInvalidateSession(HttpServletRequest request,
HttpServletResponse response) {
response.setHeader(this.headerName, "");
}
/**
* The name of the header to obtain the session id from. Default is "x-auth-token".
*
* @param headerName the name of the header to obtain the session id from.
*/
public void setHeaderName(String headerName) {
Assert.notNull(headerName, "headerName cannot be null");
this.headerName = headerName;
}
}
一些特别的鉴权场景,需要由应用层自己控制生成sessionid,那么先看看源码~
public final class MapSession implements ExpiringSession, Serializable {
/**
* Default {@link #setMaxInactiveIntervalInSeconds(int)} (30 minutes).
*/
public static final int DEFAULT_MAX_INACTIVE_INTERVAL_SECOnDS= 1800;
private String id;
private Map sessiOnAttrs= new HashMap();
private long creatiOnTime= System.currentTimeMillis();
private long lastAccessedTime = this.creationTime;
/**
* Defaults to 30 minutes.
*/
private int maxInactiveInterval = DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
/**
* Creates a new instance with a secure randomly generated identifier.
*/
public MapSession() {
this(UUID.randomUUID().toString());
}
/**
* Creates a new instance with the specified id. This is preferred to the default
* constructor when the id is known to prevent unnecessary consumption on entropy
* which can be slow.
*
* @param id the identifier to use
*/
public MapSession(String id) {
this.id = id;
}
看org.springframework.session.MapSession发现,新建session的时候,构造 方法默认就是使用UUID做id,并调用MapSession(String id)构造方法给id赋值。
在看org.springframework.session.SessionRepository接口:
public interface SessionRepositoryextends Session> {
S createSession();
void save(S session);
S getSession(String id);
void delete(String id);
}
发现并没有S createSession(String id);的方法,后面通过改源码的方式实现了自定义id的功能。加了一个S createSession(String id);的方法,并通过getRequestedSessionId();把id拿过来传参。这里就不详讲了,因为这种方式特别不友好。
Another way:
可以自定义一个session策略,在用户id和sessionid之间加一个映射保存在redis,在onNewSession()创建映射关系,在getRequestedSessionId()时找用户id对应的sessionid返回。
下面是MyHttpSessionStrategy代码,HttpSessionConfig里面的httpSessionStrategy()改为自己写的就可以了。
public class MyHttpSessionStrategy implements HttpSessionStrategy {
private final Logger logger = LoggerFactory.getLogger(WlwHttpSessionStrategy.class);
//这用Qualifier注解,如果你的工程还集成了spring-data-redis,需要指定一下用哪一个
@Qualifier("sessionRedisTemplate")
@Autowired
private RedisTemplate redisTemplate;
//过期时间,与session过期时间保持一致
private Long maxInactiveIntervalInSecOnds= 999L;
private String xxxRedisName = "spring:session:xxx:";
//当客户端没有传xxx参数的时候,避免创建多个无用的session占用redis空间
private String defaultSessiOnId= "default-sessionid";
/**
* 客户端传过来的是xxx,需要通过xxx查找映射关系,拿到sessionid返回
*/
public String getRequestedSessionId(HttpServletRequest request) {
String xxx = request.getParameter("xxx");
ValueOperations vops = redisTemplate.opsForValue();
if (xxx != null && !xxx.equals("")) {
String sessionid = vops.get(xxxRedisName + xxx);
if(sessionid!=null){
redisTemplate.expire(xxxRedisName + xxx, maxInactiveIntervalInSeconds, TimeUnit.SECONDS);
}
return sessionid;
} else {
return vops.get(xxxRedisName+defaultSessionId);
}
}
/**
* 创建session时,保存xxx和sessionid的映射关系
*/
public void onNewSession(Session session, HttpServletRequest request,
HttpServletResponse response) {
String xxx = request.getParameter("xxx");
String sessionid = session.getId();
ValueOperations vops = redisTemplate.opsForValue();
if (xxx != null && !xxx.equals("")) {
//保存xxx和sessionid映射关系
vops.set(xxxRedisName + xxx, sessionid);
redisTemplate.expire(xxxRedisName + xxx, maxInactiveIntervalInSeconds, TimeUnit.SECONDS);
}else{
//没有传xxx时,保存为默认
vops.set(xxxRedisName+defaultSessionId, sessionid);
redisTemplate.expire(xxxRedisName+defaultSessionId, maxInactiveIntervalInSeconds, TimeUnit.SECONDS);
}
}
public void onInvalidateSession(HttpServletRequest request,
HttpServletResponse response) {
String xxx = request.getParameter("xxx");
redisTemplate.expire(xxxRedisName + xxx, 0, TimeUnit.SECONDS);
}
}
好了,完工了。。。