Spring Security - 会话管理

会话管理

由于HTTP协议是无状态协议,对于服务器而言每个请求都一样,缺少一个状态去区分请求是否来自于不同的用户,以便服务器提供不同的服务。

所以我们需要利用某种机制来记录不同用户的标识信息。这个机制就是Session

这个时候cookie就体现了它的重要作用

当客户端首次请求服务端时

服务端为该用户生成一个sessionId,并保存在cookie中,带回客户端,客户端保存这个cookie

之后客户端每次请求都带上这个cookie

服务端可以很容易区分是来自哪个用户的请求。

但出于安全考虑,有时用户在浏览器中禁用cookie,这个时候可以利用URL重新,将sessionId拼接在重新的URL后面返回给已经授权的用户。

一、 防御会话固定攻击

攻击者自己正常访问系统,系统给攻击者分配了一个sessionId

攻击者拿着自己手上的sessionId伪造一个系统登录链接

受害者利用链接登陆了,那么sessionId绑定了用户的信息

攻击者可以利用手里的sessionId冒充受害者

这就是会话固定攻击

当我们登录成功之后重新生成新的session,即可避免

Spring Security自带该功能,并且自带的HTTP防火墙会帮我们拦截掉哪些拼接的不合法的URL

sessionManagement是一个会话管理的配置器,其中,防御会话固定攻击的策略有四种:

  • none:不做任何变动,登录之后沿用旧的session

  • newSession:登录之后创建一个新的session

  • migrateSession:登录之后创建一个新的session,并将旧的session中的数据复制过来。

  • changeSessionId:不创建新的会话,而是使用由Servlet容器提供的会话固定保护。

默认已经启用migrateSession策略,如有必要,可以做出修改。

@Override
protected void configure(HttpSecurity http) throws Exception {
    JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
    jdbcTokenRepository.setDataSource(dataSource);
    http.authorizeRequests()
        .antMatchers("/admin/api/**").hasRole("ADMIN")
        .antMatchers("/user/api/**").hasRole("USER")
        .antMatchers("/app/api/**", "/captcha.jpg").permitAll()
        .anyRequest()
        .authenticated()
        .and()
        .formLogin()
        //AuthenticationDetailsSource
        //                .authenticationDetailsSource(myWebAuthenticationDetailsSource)
        .loginPage("/myLogin.html")
        // 指定处理登录请求的路径,修改请求的路径,默认为/login
        .loginProcessingUrl("/mylogin").permitAll()
        .failureHandler(new MyAuthenticationFailureHandler())
        .and()
        //增加自动登录功能,默认为散列加密
        .rememberMe()
        .userDetailsService(myUserDetailsService)
        .tokenRepository(jdbcTokenRepository)
        //设置sessionManagement策略
        .and()
        .sessionManagement()
        .sessionFixation()
        .none()
        .and()
        .csrf().disable();
        //将过滤器添加在UsernamePasswordAuthenticationFilter之前
    http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
}

二、会话过期

可以通过配置会话过期策略

  • 过期跳转

    .sessionManagement()
      .invalidSessionUrl("/")
    
  • 过期时间

    # 单位秒,最低限制60秒,小于60会被修正为60
    server:
      servlet:
        session:
          timeout: 90
    

三、会话并发控制

  • 异地登录踢掉当前登录用户

    .sessionManagement()
        //设置最大会话数为1
        .maximumSessions(1)
    

    在其它客户端重新登录会挤掉之前登录的账号

    并且之前页面会显示

    This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).
    

    具体实现可看ConcurrentSessionControlAuthenticationStrategy类源码。

  • 已经登录,禁止异地登录

    .sessionManagement()
        //设置最大会话数为1
        .maximumSessions(1)
        //阻止新会话登录,默认为false
        .maxSessionsPreventsLogin(true)
    

    异地登录报错

    {
       "error_code": 401,
       "error_name":"org.springframework.security.web.authentication.session.SessionAuthenticationException",
       "message": "请求失败,Maximum sessions of 1 for this principal exceeded"
    }
    

    看似好像已经没有问题,但是我们将原来登录的用户注销(通过请求/logout),然后再去登录,发现任然登不上

这是因为通过监听session的销毁来触发会话信息的表相关清理工作,但我们还没有注册过相关的监听器,所以导致Spring Security无法正常清理过期或已注销的会话。

在Servlet中,监听session相关事件的方法是实现HttpSessionListener接口,并在系统中注册该监听 器。

Spring SecurityHttpSessionEventPublisher类中实现HttpSessionEventPublisher接口,并转化成 Spring的事件机制。

Spring事件机制中,事件的发布、订阅都交由Spring容器来托管,我们可以很方便地通过注册 bean的方式来订阅关心的事件。

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher(){
    return new HttpSessionEventPublisher();
}

注意:principals采用了以用户信息为key的设计,必须覆写UserhashCodeequals两个方法

四、集群会话解决方案

当系统采用集群部署时,通常请求会先集中在一个中间件上(Nginx),再通过其转发到对应服务上,达到负载均衡的目的。

这样就会出现,我在A服务已经登录过,但是当请求被转发到B的时候,用户又要重新登录,这就是典型的会话状态集群不同步问题。

常见解决方案:

  • session保持
    • 通常采用IP哈希负载策略将来自相同客户端的请求转发至相同的服务器上进行处理。
    • 存在一定程度的负载失衡
  • session复制
    • session复制是指在集群服务器之间同步session数据,以达到各个实例之间会话状态一致的做法。
    • 消耗数据带宽,还会占用大量的资源。
  • session共享
    • session 共享是指将 session 从服务器内存抽离出来,集中存储到独立的数据容器,并由各个服务器共享。
    • 独立的数据容器增加了网络交互,数据容器的读/写性能、稳定性以及网络I/O速度都成为性能的瓶颈。

五、整合Spring Session解决集群会话问题

session共享,本质上就是存储容器的变动

Spring Session支持多种类型的存储容器

基于Redis整合

为工程引入依赖

<!--spring session对接Redis必要依赖-->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
<!--spring boot对接Redis必要依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

之后就可以配置Spring Session了,主要是为Spring Security提供集群支持的会话注册表。

修改配置文件

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/springSecurityDemo?useUnicode=true&&characterEncoding=utf8&&useSSL=false&&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
  redis:
    host: 127.0.0.1
    port: 6379
    database: 2
    timeout: 1000s
  session:
    store-type: redis
    timeout: 1800000

重启项目登录

通过Redis客户端查看

image-20201023145928025.png

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351