【发布时间】:2018-02-03 17:15:12
【问题描述】:
我正在尝试在我的 Spring Boot 应用程序中实现“强制注销”功能(例如,在管理员禁用用户帐户后)。
我按照各种教程中指定的步骤来访问会话注册表,以使用户的会话到期(baeldung step 6 和 verbose version on myyurt;还有related SO)。
但是,在将 SessionRegistryImpl 注册为@Bean 后,在使用调试器时,我看到依赖注入机制中有 2 个不同的实例:
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":
[
]
},
如果都声明为 Singleton,DI 系统中怎么会有两个不同的实例? 您对可能出现的问题有任何提示吗?
我正在使用 spring-boot-starter-parent 1.5.2.RELEASE,它使用 Spring Security 4.2.2.RELEASE。
【问题讨论】:
-
不知道有没有关系,但是
@Bean注解的方法不需要static。由于 Spring 在配置类中使用代理等,这可能是一个问题,但我不确定。也许尝试删除静态关键字。 -
我在春天遇到过这样的问题。你能显示引用 SessionRegistries 的对象吗?就我而言,应用程序中有上下文。
-
实际上我认为静态关键字是问题所在,因为在您调用
.sessionRegistry(sessionRegistry())的配置中,它会直接调用静态方法,而不是通过 Spring 代理从应用程序中获取 bean语境。这意味着,对于您的安全配置,您需要创建一个新实例,而不是从应用程序上下文中获取 bean。 -
谢谢@dunni,就是这样。我删除了静态字段,它现在可以工作了……我没想到。如果您愿意,请将其发布为答案,我会接受。干杯!
标签: java spring spring-mvc spring-boot spring-security