zxpnet网站 zxpnet网站
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

zxpnet

一个爱学习的java开发攻城狮
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 大后端课程视频归档
  • 南航面试题
  • 并发编程

  • 性能调优

  • java8语法

  • lombok

  • 日志

  • 工具类

  • spring

  • mybatis

  • springboot

  • redis

  • zookeeper

  • springcloud

  • dubbo

  • netty

  • springsecurity

    • springsecurity基本使用及个性化登录配置
    • 微服务安全与实战

    • springsecurity认证流程分析
    • springsecurity过滤器链分析
    • springsecurity图形验证码
    • springsecurity记住我RememberMe功能
    • springsecurity手机验证码登陆
    • springsecurity httpFirewall
    • springsecurity漏洞保护
    • Untitled
    • 跨域问题
    • Springsecurity Session会话管理
      • Session超时处理
      • Session并发控制
        • 控制一个账号同一时刻最多能登录多少个
        • Session达到最大有效数的时候,不再允许相同的账户登录
        • 原理分析
      • Session集群处理
        • starter分析
        • session序列化问题
      • 踢出用户(让Session失效)
      • 获取所有Session信息:
    • springsecurity控制授权
    • 权限模型
    • springseuciryt oauth2基础
    • 基于JWT实现SSO单点登录
    • springsocial第三方登陆
    • springsecurity退出登陆
    • Spring security 集成 JustAuth 实现第三方授权登录
    • Spring Boot+CAS 单点登录
    • Springsecurity常见错误
  • mq消息中间件

  • shiro

  • beetle

  • 模板引擎

  • jpa

  • 数据结构与算法

  • 数据库知识与设计

  • gradle

  • maven

  • bus

  • 定时任务

  • docker

  • centos

  • 加解密

  • biz业务

  • pigx项目

  • 开源项目

  • 品达通用权限项目-黑马

  • 货币交易项目coin-尚学堂

  • php

  • backend
  • springsecurity
shollin
2021-07-04
目录

Springsecurity Session会话管理

  • Session超时处理
  • Session并发控制
    • 控制一个账号同一时刻最多能登录多少个
    • Session达到最大有效数的时候,不再允许相同的账户登录
    • 原理分析
  • Session集群处理
    • starter分析
    • session序列化问题
  • 踢出用户(让Session失效)
  • 获取所有Session信息:

用户登录成功后,信息保存在服务器Session中,这节学习下如何管理这些Session。这节将在Spring Security短信验证码登录的基础上继续扩展。

# Session超时处理

Session超时时间也就是用户登录的有效时间。要设置Session超时时间很简单,只需要在配置文件中添加:

server.servlet.session.timeout=30m
1

单位为秒,通过上面的配置,Session的有效期为一个小时。默认为30m分钟,最少为1分钟,如果设置小于1分钟,也会被置为1分钟,见源码TomcatServletWebServerFactory第426行

private long getSessionTimeoutInMinutes() {
		Duration sessionTimeout = getSession().getTimeout();
		if (isZeroOrLess(sessionTimeout)) {
			return 0;
		}
		return Math.max(sessionTimeout.toMinutes(), 1);
	}
1
2
3
4
5
6
7

Session失效后,刷新页面后将跳转到认证页面,我们可以再添加一些配置,自定义Session失效后的一些行为。

在Spring Security中配置Session管理器,并配置Session失效后要跳转的URL:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器
        .addFilterBefore(smsCodeFilter,UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器
            .formLogin() // 表单登录
                .loginPage("/authentication/require") // 登录跳转 URL
                .loginProcessingUrl("/login") // 处理表单登录 URL
                .successHandler(authenticationSucessHandler) // 处理登录成功
                .failureHandler(authenticationFailureHandler) // 处理登录失败
            .and()
                .authorizeRequests() // 授权配置
                .antMatchers("/authentication/require",
                        "/login.html", "/code/image","/code/sms","/session/invalid").permitAll() // 无需认证的请求路径
                .anyRequest()  // 所有请求
                .authenticated() // 都需要认证
            .and()
                .sessionManagement() // 添加 Session管理器
                .invalidSessionUrl("/session/invalid") // Session失效后跳转到这个链接
            ......
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@GetMapping("session/invalid")
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public String sessionInvalid(){
    return "session已失效,请重新认证";
}

1
2
3
4
5
6

# Session并发控制

# 控制一个账号同一时刻最多能登录多少个

我们在Spring Security配置中继续添加Session相关配置:

@Autowired
private MySessionExpiredStrategy sessionExpiredStrategy;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器
        .addFilterBefore(smsCodeFilter,UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器
            .formLogin() // 表单登录
                .loginPage("/authentication/require") // 登录跳转 URL
                .loginProcessingUrl("/login") // 处理表单登录 URL
                .successHandler(authenticationSucessHandler) // 处理登录成功
                .failureHandler(authenticationFailureHandler) // 处理登录失败
            .and()
                .authorizeRequests() // 授权配置
                .antMatchers("/authentication/require",
                        "/login.html", "/code/image","/code/sms","/session/invalid").permitAll() // 无需认证的请求路径
                .anyRequest()  // 所有请求
                .authenticated() // 都需要认证
            .and()
                .sessionManagement() // 添加 Session管理器
                .invalidSessionUrl("/session/invalid") // Session失效后跳转到这个链接
                .maximumSessions(1)
                .expiredSessionStrategy(sessionExpiredStrategy)
                .and()
            ......

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

maximumSessions配置了最大Session并发数量为1个,如果mrbird这个账户登录后,在另一个客户端也使用mrbird账户登录,那么第一个使用mrbird登录的账户将会失效,类似于一个先入先出队列。expiredSessionStrategy配置了Session在并发下失效后的处理策略,这里为我们自定义的策略MySessionExpiredStrategy。

MySessionExpiredStrategy实现SessionInformationExpiredStrategy:

@Component
public class MySessionExpiredStrategy implements SessionInformationExpiredStrategy {

    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
        HttpServletResponse response = event.getResponse();
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write("您的账号已经在别的地方登录,当前登录已失效。如果密码遭到泄露,请立即修改密码!");
    }
}

1
2
3
4
5
6
7
8
9
10
11
12

为了演示这个效果,我们先将Session超时时间设置久一点,比如3600秒,然后重启项目,在Chrome里使用mrbird账户登录。

登录成功后,在firefox上也是用mrbird账户登录,登录成功后回到chrome,刷新页面,效果如下所示:

QQ截图20190618105958.png

# Session达到最大有效数的时候,不再允许相同的账户登录

要实现这个功能只需要在上面的配置中添加:

......
.and()
    .sessionManagement() // 添加 Session管理器
    .invalidSessionUrl("/session/invalid") // Session失效后跳转到这个链接
    .maximumSessions(1)
    .maxSessionsPreventsLogin(true)
    .expiredSessionStrategy(sessionExpiredStrategy)
    .and()
......

1
2
3
4
5
6
7
8
9
10

在实际开发中,发现Session并发控制只对Spring Security默认的登录方式——账号密码登录有效,而像短信验证码登录,社交账号登录并不生效,解决方案可以参考开源项目https://github.com/wuyouzhuguli/FEBS-Security

# 原理分析

Spring Security 中通过 SessionRegistryImpl 类来实现对会话信息的统一管理,我们来看下这个类的源码(部分):

# Session集群处理

Session集群听着高大上,其实实现起来很简单。当我们登录成功后,用户认证的信息存储在Session中,而这些Session默认是存储在运行运用的服务器上的,比如Tomcat,netty等。当应用集群部署的时候,用户在A应用上登录认证了,后续通过负载均衡可能会把请求发送到B应用,而B应用服务器上并没有与该请求匹配的认证Session信息,所以用户就需要重新进行认证。要解决这个问题,我们可以把Session信息存储在第三方容器里(如Redis集群),而不是各自的服务器,这样应用集群就可以通过第三方容器来共享Session了。

image-20210709075258920

我们引入Redis和Spring Session依赖:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- session存放到redis当中 -->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

1
2
3
4
5
6
7
8
9
10
11
spring:
  session:
    redis:
      flush-mode: on_save
      namespace: session.example
      cleanup-cron: 0 * * * * *
    store-type: redis
    timeout: 1800
  redis:
    host: localhost
    port: 6379
    jedis:
      pool:
        max-active: 100
        max-wait: 10
        max-idle: 10
        min-idle: 10
    database: 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# starter分析

主要的类:

SessionAutoConfiguration 自动配置,定义了cookie、rememberMe等的序列化器

SessionProperties 配置信息, 默认失效时间30分钟,redis命名空间spring:session

RedisHttpSessionConfiguration : 注册了RedisIndexedSessionRepository, EnableRedisHttpSession注解引入

RedisWebSessionConfiguration: 注册了ReactiveRedisSessionRepository, EnableRedisWebSession注解引入

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({Session.class})
@ConditionalOnWebApplication
@EnableConfigurationProperties({ServerProperties.class, SessionProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, HazelcastAutoConfiguration.class, JdbcTemplateAutoConfiguration.class, MongoDataAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class, RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class})
@AutoConfigureBefore({HttpHandlerAutoConfiguration.class})
public class SessionAutoConfiguration {
}
1
2
3
4
5
6
7
8
9
10

image-20210709080821707

image-20210709083645200

# session序列化问题

保存到redis当中,需要序列化,因此保存到session里面的数据,需要可序列化,实现Serializable接口

验证码 BufferedImage不可序列化

# 踢出用户(让Session失效)

String currentSessionId = request.getRequestedSessionId();
sessionRegistry.getSessionInformation(sessionId).expireNow();
1
2

# 获取所有Session信息:

List<Object> principals = sessionRegistry.getAllPrincipals();

1
2
跨域问题
springsecurity控制授权

← 跨域问题 springsecurity控制授权→

最近更新
01
国际象棋
09-15
02
成语
09-15
03
自然拼读
09-15
更多文章>
Theme by Vdoing | Copyright © 2019-2023 zxpnet | 粤ICP备14079330号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式