Re:从零开始的Spring Session(三)

上一篇文章中,我们使用Redis集成了Spring Session。大多数的配置都是Spring Boot帮我们自动配置的,这一节我们介绍一点Spring Session较为高级的特性。

集成Spring Security

之所以把Spring Session和Spring Security放在一起讨论,是因为我们的应用在集成Spring Security之后,用户相关的认证与Session密不可分,如果不注意一些细节,会引发意想不到的问题。

与Spring Session相关的依赖可以参考上一篇文章,这里给出增量的依赖:

1234
<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-security</artifactId></dependency>

我们引入依赖后,就已经自动配置了Spring Security,我们在application.yml添加一个内存中的用户:

1234
security:  user:    name: admin    password: admin

测试登录点沿用上一篇文章的端点,访问http://localhost:8080/test/cookie?browser=chrome端点后会出现http basic的认证框,我们输入admin/admin,即可获得结果,也遇到了第一个坑点,我们会发现每次请求,sessionId都会被刷新,这显然不是我们想要的结果。

诡异的运行结果

这个现象笔者研究了不少源码,但并没有得到非常满意的解释,只能理解为SecurityAutoConfiguration提供的默认配置,没有触发到响应的配置,导致了session的不断刷新(如果读者有合理的解释可以和我沟通)。Spring Session之所以能够替换默认的tomcat httpSession是因为配置了springSessionRepositoryFilter这个过滤器,且提供了非常高的优先级,这归功于AbstractSecurityWebApplicationInitializer ,AbstractHttpSessionApplicationInitializer 这两个初始化器,当然,也保证了Spring Session会在Spring Security之前起作用。

而解决上述的诡异现象也比较容易(但原理不清),我们使用@EnableWebSecurity对Spring Security进行一些配置,即可解决这个问题。

1234567891011121314151617181920212223242526
@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {

    // @formatter:off    @Override    protected void configure(HttpSecurity http) throws Exception {        http            .authorizeRequests()            .antMatchers("/resources/**").permitAll()            .anyRequest().authenticated()            .and()                .httpBasic()//<1>            .and()            .logout().permitAll();    }    // @formatter:on

    // @formatter:off    @Autowired    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {        auth                .inMemoryAuthentication()                .withUser("admin").password("admin").roles("USER");//<2>    }    // @formatter:on}

<1> 不想大费周章写一个登录页面,于是开启了http basic认证

<2> 配置了security config之后,springboot的autoConfig就会失效,于是需要手动配置用户。

再次请求,可以发现SessionId返回正常,@EnableWebSecurity似乎触发了相关的配置,当然了,我们在使用Spring Security时不可能使用autoconfig,但是这个现象的确是一个疑点。

使用自定义CookieSerializer

12345678
@Beanpublic CookieSerializer cookieSerializer() {    DefaultCookieSerializer serializer = new DefaultCookieSerializer();    serializer.setCookieName("JSESSIONID");    serializer.setCookiePath("/");    serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");    return serializer;}

使用上述配置后,我们可以将Spring Session默认的Cookie Key从SESSION替换为原生的JSESSIONID。而CookiePath设置为根路径且配置了相关的正则表达式,可以达到同父域下的单点登录的效果,在未涉及跨域的单点登录系统中,这是一个非常优雅的解决方案。如果我们的当前域名是moe.cnkirito.moe,该正则会将Cookie设置在父域cnkirito.moe中,如果有另一个相同父域的子域名blog.cnkirito.moe也会识别这个Cookie,便可以很方便的实现同父域下的单点登录。

根据用户名查找用户归属的SESSION

这个特性听起来非常有意思,你可以在一些有趣的场景下使用它,如知道用户名后即可删除其SESSION。一直以来我们都是通过线程绑定的方式,让用户操作自己的SESSION,包括获取用户名等操作。但如今它提供了一个反向的操作,根据用户名获取SESSION,恰巧,在一些项目中真的可以使用到这个特性,最起码,当别人问起你,或者讨论到和SESSION相关的知识时,你可以明晰一点,这是可以做到的。

我们使用Redis作为Session Store还有一个好处,就是其实现了FindByIndexNameSessionRepository接口,下面让我们来见证这一点。

123456789101112
@Controllerpublic class CookieController {    @Autowired    FindByIndexNameSessionRepository<? extends ExpiringSession> sessionRepository;

    @RequestMapping("/test/findByUsername")    @ResponseBody    public Map findByUsername(@RequestParam String username) {        Map<String, ? extends ExpiringSession> usersSessions = sessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);        return usersSessions;    }}

由于一个用户可能拥有多个Session,所以返回的是一个Map信息,而这里的username,则就是与Spring Security集成之后的用户名,最令人感动Spring厉害的地方,是这一切都是自动配置好的。我们在内存中配置的用户的username是admin,于是我们访问这个端点,可以看到如下的结果

用户名访问session

连同我们存入session中的browser=chrome,browser=360都可以看见(只有键名)。

总结

Spring Session对各种场景下的Session管理提供一套非常完善的实现。笔者所介绍的,仅仅是Spring Session常用的一些特性,更多的知识点可以在spring.io的文档中一览无余,以及本文中作者存在的一个疑惑,如有兴趣可与我沟通。

https://www.cnkirito.moe/spring-session-3/

原文地址:https://www.cnblogs.com/softidea/p/10323293.html

时间: 2024-10-10 12:29:57

Re:从零开始的Spring Session(三)的相关文章

Re:从零开始的Spring Session(二)

上一篇文章介绍了一些Session和Cookie的基础知识,这篇文章开始正式介绍Spring Session是如何对传统的Session进行改造的.官网这么介绍Spring Session: Spring Session provides an API and implementations for managing a user’s session information. It also provides transparent integration with: HttpSession -

Re:从零开始的Spring Session(一)

Session和Cookie这两个概念,在学习java web开发之初,大多数人就已经接触过了.最近在研究跨域单点登录的实现时,发现对于Session和Cookie的了解,并不是很深入,所以打算写两篇文章记录一下自己的理解.在我们的应用集成Spring Session之前,先补充一点Session和Cookie的关键知识. Session与Cookie基础 由于http协议是无状态的协议,为了能够记住请求的状态,于是引入了Session和Cookie的机制.我们应该有一个很明确的概念,那就是Se

使用Spring Session做分布式会话管理

在Web项目开发中,会话管理是一个很重要的部分,用于存储与用户相关的数据.通常是由符合session规范的容器来负责存储管理,也就是一旦容器关闭,重启会导致会话失效.因此打造一个高可用性的系统,必须将session管理从容器中独立出来.而这实现方案有很多种,下面简单介绍下: 第一种是使用容器扩展来实现,大家比较容易接受的是通过容器插件来实现,比如基于Tomcat的tomcat-redis-session-manager,基于Jetty的jetty-session-redis等等.好处是对项目来说

Spring session redis

http://www.open-open.com/lib/view/open1436322883958.html 一: 新建maven的webapp项目,加入spring session的相关依赖 pom.xml 如下: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocati

21. Spring Boot过滤器、监听器【从零开始学Spring Boot】

转载:http://blog.csdn.net/linxingliang/article/details/52069490 上一篇文章已经对定义Servlet 的方法进行了说明,过滤器(Filter)和 监听器(Listener)的注册方法和 Servlet 一样,不清楚的可以查看下上一篇文章(20): 本文将直接使用@WebFilter和@WebListener的方式,完成一个Filter 和一个 Listener:使用注解 @ServletComponentScan//这个就是扫描相应的Se

大话 Spring Session 共享

javaweb中我们项目稍微正规点,都会用到单点登录这个技术.实现它的方法各家有各界的看法.这几天由于公司项目需求正在研究.下面整理一下最近整理的心得. 简介 在分布式项目中另我们头疼的是多项目之间的数据共享(即Session共享),经常会出现数据丢失的情况.为了解决这种Bug.前人已经把我们实现了两种解决的办法.今天我在这里一下这两种方式.侧重偏向第二种方法 配tomcat实现session共享 资源下载 点我下载 配置Tomcat 下载好上面的jar包,把他放在tomcat的lib文件下.

利用spring session解决共享Session问题

1.共享Session问题 HttpSession是通过Servlet容器创建和管理的,像Tomcat/Jetty都是保存在内存中的.而如果我们把web服务器搭建成分布式的集群,然后利用LVS或Nginx做负载均衡,那么来自同一用户的Http请求将有可能被分发到两个不同的web站点中去.那么问题就来了,如何保证不同的web站点能够共享同一份session数据呢? 最简单的想法就是把session数据保存到内存以外的一个统一的地方,例如Memcached/Redis等数据库中.那么问题又来了,如何

Spring Session在Spring MVC中的使用.md

Web项目会通过Session进行会话保持,Session是保存在服务器内存中: 现在为了提高站点的性能和稳定性,将Web项目发布到多个服务器,通过代理如Nginx或F5做负载均衡: 由于负载均衡正常配置,会对客户端的请求随机转发到某一个服务器,这会导致Session丢失: 解决方案:一种是可将代理如Nginx或F5配置为高可用,即用户访问时,在同一会话期间内,只往一台服务器转发:另一种是引入Spring Session,对Session进行持久化.统一管理: Spring Session对Se

springboot+spring session+redis+nginx实现session共享和负载均衡

环境 centos7. jdk1.8.nginx.redis.springboot 1.5.8.RELEASE session共享 添加spring session和redis依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> &