问题
简单使用Spring Security实现简单单点登录。
思路
引入Spring Security ,Spring Session Redis相关库,简单配置Spring Security实现对用户的单点登录。
解决
Maven
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
</dependency>
这里需要引入Spring Session Redis库,这样的话,Spring Security就会默认使用Redis进行集成会话管理。这里主要关注Spring Security的使用,Spring Session Redis只介绍简单的配置。
Spring Session
spring.session.redis.flush-mode=on_save
spring.session.redis.namespace=xxx:session
spring.session.timeout=P30D
spring.data.redis.host=xxx.xxx.xxx.xxx
spring.data.redis.port=6379
spring.data.redis.password=${REDIS_PW}
spring.data.redis.database=0
这都是一些redis连接的简单配置,这里的redis主要作用为集中会话的存储。
Spring Security
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;import static org.springframework.security.config.Customizer.withDefaults;@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfiguration {@Beanpublic WebSecurityCustomizer ignoringCustomizer() {# 忽略的请求路径return (web) -> web.ignoring().requestMatchers("/users/index");}@Beanpublic UserDetailsService userDetailsService() {# 简单创建用户认证InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build());return manager;}@Beanpublic SpringSessionRememberMeServices rememberMeServices() {# 需要记住用户会话SpringSessionRememberMeServices rememberMeServices =new SpringSessionRememberMeServices();// optionally customizerememberMeServices.setAlwaysRemember(true);return rememberMeServices;}@Beanpublic HttpSessionEventPublisher httpSessionEventPublisher() {return new HttpSessionEventPublisher();}@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.cors().and().csrf().and().sessionManagement(session -> session.maximumSessions(1) # 启用Spring Security的简单SSO控制).rememberMe((rememberMe) -> rememberMe.rememberMeServices(rememberMeServices())).authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()).formLogin(withDefaults()) # 启用 form 方式登录.httpBasic().disable();# 禁用 http basic方式登录return http.build();}
}
总结
到这里,就基本实现了Spring Security的简单SSO,达到的效果,就是用户用一个浏览器登录成功后,如果在另外一个浏览器上面再次登录,会导致把第一次登录的会话失效。这里的Spring Session Redis仅仅是提供一个集中会话管理。这里有个问题就是Spring Security发现用户如果需要登录,会默认让浏览器跳转到Spring Secuity的默认登录页面,这种作为rest api风格的后台服务非常不友好。下次我们再解决这个问题。值得注意的是@EnableMethodSecurity
注解,Spring Security提供了丰富的方法层面的流程控制。
参考:
- Session Management
- Spring Security without the WebSecurityConfigurerAdapter
- Breaking Changes
- SpringBoot之SpringSecurity权限注解在方法上进行权限认证多种方式