spring-session实现分布式session共享及自定义sessionid

官方文档地址: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.session</groupId>  
                <artifactId>spring-session-data-redis</artifactId>  
                <version>1.2.2.RELEASE</version>  
                <type>pom</type>  
</dependency>  
<dependency>  
                <groupId>org.springframework</groupId>  
                <artifactId>spring-web</artifactId>  
                <version>4.2.5.RELEASE</version>  
</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吧~
一些特别的鉴权场景,需要由应用层自己控制生成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<String, Object> sessionAttrs = new HashMap<String, Object>();  
    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 SessionRepository<S extends 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<String, String> 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<String, String> 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);  
    }  
  
} 

好了,完工了。。。

posted @ 2017-12-20 16:45  门罗的魔术师  阅读(2684)  评论(0编辑  收藏  举报