Spring Security框架中踢人下线技术探索

1.背景在某次项目的开发中,使用到了Spring Security权限框架进行后端权限开发的权限校验,底层集成Spring Session组件,非常方便的集成Redis进行分布式Session的会话集群部署。系统正式上线后,各个部署节点能够非常方便的进行集群部署,用户的Session会话信息全部保存在Redis中间件库中,开发者不用关心具体的实现,Spring Session组件已经全部集成好了。但是在系统的用户管理模块中,提供了对系统用户账号的删除功能以及禁用功能,针对这两个功能,需求方给出的具体要求
摘要由CSDN通过智能技术生成

1.背景

在某次项目的开发中,使用到了Spring Security权限框架进行后端权限开发的权限校验,底层集成Spring Session组件,非常方便的集成Redis进行分布式Session的会话集群部署。系统正式上线后,各个部署节点能够非常方便的进行集群部署,用户的Session会话信息全部保存在Redis中间件库中,开发者不用关心具体的实现,Spring Session组件已经全部集成好了。

但是在系统的用户管理模块中,提供了对系统用户账号的删除功能以及禁用功能,针对这两个功能,需求方给出的具体要求是:

  • 删除:当管理员删除当前用户账号时,如果当前账号已经登录系统,则需要剔除下线,并且不可登录
  • 禁用:当管理员对当前账号禁用操作时,如果当前账号已经登录系统,则需要剔除下线,并且登录时,提示当前账号已禁用

2.需求分析

从上面的需求来看,不管是删除还是禁用功能,都需要实现,如果当前账号已经登录系统,则需要剔除下线,而禁用操作只需要再登录时给出提示信息即可,这个在业务登录方法中就可以实现,不必从底层框架进行考虑。

因此,从底层技术测进行考虑时,我们需要探索如何在Spring Security权限框架中实现踢人下线的功能。

既然需求已经明确,从功能的实现可能性方面入手,我们则需要从几个方面进行考虑:

  • 1)、在Spring Security框架中,用户登录的Session会话信息存储在哪里?
  • 2)、在Spring Security框架中,Session会话如何存储,主要存储哪些信息?
  • 3)、如何根据账号收集当前该账号登录的所有Session会话信息?
  • 4)、如何在服务端主动销毁Session对象?

1)、在Spring Security框架中,用户登录的Session会话信息存储在哪里?

如果我们不考虑分布式Session会话的情况,单体Spring Boot项目中,服务端Session会话肯定存储在内存中,这样的弊端是如果当前应用需要做负载均衡进行部署时,用户请求服务端接口时,会存在Session会话丢失的情况,因为用户登录的会话信息都存在JVM内存中,没有进程之间的共享互通。

为了解决分布式应用Session会话不丢失的问题,Spring Session组件发布了,该组件提供了基于JDBC\Redis等中间件的方式,将用户端的Session会话存储在中间件中,这样分布式应用获取用户会话时,都会从中间件去获取会话Session,这样也保证了服务可以做负载部署以保证Session会话不丢失。本文主要讨论的也是这种情况,集成Redis中间件用来保存用户会话信息。

2)、在Spring Security框架中,Session会话如何存储,主要存储哪些信息?

由于我们使用了Redis中间件,所以,在Spring Security权限框架中产生的Session会话信息,肯定存储与Redis中,这点毫无疑问,那么存储了哪些信息呢?我会在接下来的源码分析中进行介绍

3)、如何根据账号收集当前该账号登录的所有Session会话信息?

我们从上面的需求分析中已经得知Session会话已经存储在Redis中,那么我们是否可以做这样的假设,我们只需要根据Spring Security中在Redis中存储的键值,找到和登录用户名相关的Redis缓存数据,就可以通过调用Security封装的方法进行获取,得到当前登录账号的会话信息呢?这个我们需要在源码中去找到答案

4)、如何在服务端主动销毁Session对象?

如果是单体的Spring Boot应用,Session信息肯定存储在JVM的内存中,服务端要主动销毁Session对象只需要找到Security权限框架如何存储的就可以进行删除。

在分布式的Spring Boot应用中,我们从上面已经得知Session会话信息以及存储在Redis中间件中,那么我们只需要得到当前登录的Session在Redis中的键值,就可以调用方法进行删除操作,从而主动在服务端销毁Session会话

3.源码分析

在上面的需求分析中,我们已经提出了假设,并且根据假设,做出来技术性的判断,接下来我们需要从Spring Security以及Spring Session组件的源码中,去寻找我们需要的答案。

首先,我们在源码分析前,我们需要找到入口,也就是我们在使用Spring Security框架,并且使用Spring Session组件时,我们如何使用的。

pom.xml文件中引入组件的依赖是必不可少的,如下:

<!--Spring Security组件-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--Spring针对Redis操作组件-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--Spring Session集成Redis分布式Session会话-->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

接下来,我们在Spring Boot项目中,需要添加@EnableRedisHttpSession注解,以开启Redis组件对Session会话的支持,该注解我们需要制定Spring Session在Redis中存储的Redis命名空间,已经Session会话的时效性,示例代码如下:

@SpringBootApplication
@EnableRedisHttpSession(redisNamespace = "fish-admin:session",maxInactiveIntervalInSeconds = 7200)
public class FishAdminApplication {
   

    static Logger logger= LoggerFactory.getLogger(FishAdminApplication.class);

    public static void main(String[] args) throws UnknownHostException {
   
        ConfigurableApplicationContext application=SpringApplication.run(FishAdminApplication.class, args);
        Environment env = application.getEnvironment();
        String host= InetAddress.getLocalHost().getHostAddress();
        String port=env.getProperty("server.port");
        logger.info("\n----------------------------------------------------------\n\t" +
                        "Application '{}' is running! Access URLs:\n\t" +
                        "Local: \t\thttp://localhost:{}\n\t" +
                        "External: \thttp://{}:{}\n\t"+
                        "Doc: \thttp://{}:{}/doc.html\n\t"+
                        "----------------------------------------------------------",
                env.getProperty("spring.application.name"),
                env.getProperty("server.port"),
                host,port,
                host,port);
    }

在上面的代码中,我们指定Redis的命名空间是fish-admin:session,默认最大失效7200秒。

如果开发者默认不指定这两个属性的话,命名空间默认值是spring:session,默认最大时效则是1800

在上面我们已经说过了,既然是看源码,我们需要找到入口,这是看源码最好的方式,我们在使用Spring Session组件时,需要使用@EnableRedisHttpSession注解,那么该注解就是我们需要重点关注的对象,我们需要搞清楚,该注解的作用是什么?

EnableRedisHttpSession.java部分源码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(RedisHttpSessionConfiguration.class)
@Configuration(proxyBeanMethods = false)
public @interface EnableRedisHttpSession {
   
     //more property..   
}

在该注解中,我们可以看到,最关键的是在该注解之上,使用@Import注解导入了RedisHttpSessionConfiguration.java配置类,如果你经常翻看Spring Boot相关的源码,你会敏锐的察觉到,该配置类就是我们最终要找的类

先来看该类的UML图,如下:
在这里插入图片描述

该类实现了Spring框架中很多Aware类型接口,Aware类型的接口我们都知道,Spring容器在启动创建实体Bean后,会调用Aware系列的set方法传参赋值

当然,最核心的,我们从源码中可以看到,是Spring Session组件会向Spring容器中注入两个实体Bean,代码如下:

@Bean
public RedisIndexedSessionRepository sessionRepository() {
   
    RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
    RedisIndexedSessionRepository sessionRepository = new 
  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Security框架,可以通过以下步骤来实现下线的功能: 1. 创建一个自定义的SessionRegistry实现类,用于管理用户的会话信息。可以继承Spring Security提供的ConcurrentSessionControlStrategy类,并重写其相应方法。 ```java public class CustomSessionRegistry extends ConcurrentSessionControlStrategy { // 重写方法实现下线逻辑 } ``` 2. 在Spring Security的配置类配置SessionManagement,并指定自定义的SessionRegistry实现类。 ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .sessionManagement() .maximumSessions(1) .maxSessionsPreventsLogin(true) .sessionRegistry(sessionRegistry()); } @Bean public SessionRegistry sessionRegistry() { return new CustomSessionRegistry(); } } ``` 3. 在需要下线的地方,注入SessionRegistry,并使用其相关方法进行操作。比如,可以通过用户ID来掉指定用户的所有会话。 ```java @Autowired private SessionRegistry sessionRegistry; public void kickUser(String userId) { List<SessionInformation> sessionInfoList = sessionRegistry.getAllSessions(userId, false); for (SessionInformation sessionInfo : sessionInfoList) { sessionInfo.expireNow(); // 强制使会话失效 } } ``` 通过以上步骤,你就可以在Spring Security框架实现下线的功能了。注意,这里的示例仅供参考,具体的实现可能会根据你的业务需求而有所不同。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值