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);
    }

}
好了,完工了。。。

 心如止水,把自己学习的一点一滴全都放到了csdn上面, 
 我知道我是写给自己看的, 
 我欣赏安安静静、心无旁骛的自己, 
 尽管有些人戏谑般的说是自恋, 
 我相信, I don't care ...  尴尬


  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值