我正在努力实施强制退出'我的Spring Boot应用程序中的功能(例如,管理员禁用用户帐户后)。
我按照各种教程中指定的步骤访问会话注册表,以使用户的会话失效(baeldung step 6和verbose version on myyurt;还related SO)。
但是,在将SessionRegistryImpl注册为@Bean之后,在使用调试器时,我发现依赖注入机制中有两个不同的实例:
Spring Security在登录和注销时使用一个sessionRegistry实例,并按预期保存主体和会话。下面的截图是在登录后拍摄的 - 我在registerNewSession()方法中有一个断点。注意已登录用户的id和地图。
另一个sessionRegistry实例仅提供给我自己的SessionManager类,该类需要SessionRegistry作为依赖项并调用getAllPrincipals()。注意id是不同的,地图是空的(我多次登录后调用了getAllPrincipals()并拍了第一张截图)
我注册sessionRegistry bean的类(我删除了不必要的代码并只保留了我的自定义过滤器,以防它可能与Springs自动配置有关):
@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public static HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
@Bean
public static SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(myFirstFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(mySecondFilter, FilterSecurityInterceptor.class)
.formLogin() // skipping details
.and()
.x509() // skipping details
.and()
.logout()
.invalidateHttpSession(true)
.permitAll()
.and()
.authorizeRequests() // skipping details
.and()
.sessionManagement()
.invalidSessionUrl("/login")
.enableSessionUrlRewriting(false)
.maximumSessions(-1)
.maxSessionsPreventsLogin(false)
.sessionRegistry(sessionRegistry())
.expiredUrl("/login?expire");
}
}
我使用sessionRegistry依赖关系的类:
@Component
class DefaultSessionManager implements SessionManager {
private final SessionRegistry sessionRegistry;
@Autowired public DefaultSessionManager(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
public void expireUserSessions(String username) {
for (Object principal : sessionRegistry.getAllPrincipals()) {
// do stuff, but won't enter because the list is empty
}
}
}
如果有帮助,我用Actuator / beans端点查找bean设置,这就是它返回的内容
{
"bean": "defaultSessionManager",
"aliases":
[
],
"scope": "singleton",
"type": "com.foo.bar.DefaultSessionManager",
"resource": // file path
"dependencies":
[
"sessionRegistry"
]
},
{
"bean": "httpSessionEventPublisher",
"aliases":
[
],
"scope": "singleton",
"type": "org.springframework.security.web.session.HttpSessionEventPublisher",
"resource": "class path resource [com/foo/bar/SecurityConfig.class]",
"dependencies":
[
]
},
{
"bean": "sessionRegistry",
"aliases":
[
],
"scope": "singleton",
"type": "org.springframework.security.core.session.SessionRegistryImpl",
"resource": "class path resource [com/foo/bar/SecurityConfig.class]",
"dependencies":
[
]
},
如果DI系统声明为Singleton,那么如何在DI系统中有两个不同的实例? 你有什么可能错的提示吗?
我使用的是spring-boot-starter-parent 1.5.2.RELEASE,它使用Spring Security 4.2.2.RELEASE。
答案 0 :(得分:1)
问题是@Bean
注释方法上的静态关键字。在您调用的配置中
.sessionRegistry(sessionRegistry())
它直接调用静态方法,而不是通过Spring代理,它将从应用程序上下文中获取bean。这意味着,对于您的安全配置,您需要创建一个新实例,而不是从应用程序上下文中获取该bean。使用非静态方法,Spring会拦截同一调用中的直接方法调用,然后Spring会检查bean是否已经存在于应用程序上下文中,如果是,则返回bean。