前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Re:从零开始的Spring Session(三)

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

作者头像
kirito-moe
发布2018-04-27 11:37:09
1.2K0
发布2018-04-27 11:37:09
举报

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

集成Spring Security

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

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

代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

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

代码语言:javascript
复制
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这个过滤器,且提供了非常高的优先级,这归功于 AbstractSecurityWebApplicationInitializerAbstractHttpSessionApplicationInitializer 这两个初始化器,当然,也保证了Spring Session会在Spring Security之前起作用。

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

代码语言:javascript
复制
@EnableWebSecurity
public 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

代码语言:javascript
复制
@Bean
public 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接口,下面让我们来见证这一点。

代码语言:javascript
复制
@Controller
public 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中的browser=chrome,browser=360都可以看见(只有键名)。

总结

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-09-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Kirito的技术分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 集成Spring Security
  • 使用自定义CookieSerializer
  • 根据用户名查找用户归属的SESSION
  • 总结
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档