inb4:一般来说,我对Spring还是很陌生的,所以我知道与SpringSecurity打交道非常困难。
tl; dr:我可以从自定义的SuccessHandler到FailureHandler“抛出异常”吗?
目前,我正在尝试通过我们的登录页面实现某些行为。万一凭据错误,用户将被重定向回登录,并显示一条消息错误。如果使用的凭据正确,但是其他人已经在使用该帐户,则用户将被重定向到“登录”,并显示一条错误消息(“最大会话错误”)。
到目前为止,使用带有内置于会话/并发控件中的Springs的ExceptionMappingAuthenticationFailureHandler可以正常工作,显示正确的错误消息,但是会话控件无法正常工作。这可能是我个人的配置错误,或者仅仅是我对文档没有真正的了解(英语不是我的母语,所以我完全可以接受)。我希望从SessionControl中获得的行为是,如果与给定用户的会话处于活动状态,则试图加入该人员的人将被踢回到登录名。当前,它可以执行此操作,但是如果会话活动的用户注销并尝试重新登录,它将踢他/她使用MaxSessionsError登录。这是预期的AFAIU,因为SessionManagement.MaximumSessions()限制了帐户的登录数量,而不是用户可以同时拥有的活动会话数。我可能是错的,但我仍在调查中。我尝试设置我的配置如文档所述,但是它没有按我期望的那样工作。
我当前的替代方法是在确认其他人当前正在使用帐户时尝试在SuccessHandler上引发Exception,以便FailureHandler可以使用正确的错误消息将用户重定向到Login页面。我的理解是,如果我重定向到“登录”页面,则会话不会被破坏。我可以尝试自己销毁它,但老实说,由于我缺乏经验,因此我更相信Spring可以做到这一点,因此我提出了问题。
欢迎任何帮助或一巴掌。我想学习,到目前为止我对安全性有点疏忽。下面是config和successHandler的代码。小提示:由于某些我不十分了解的原因,该项目未使用web.xml,因此我尝试将大多数配置转换为@Bean
表示法
配置
/*Other beans and configs*/
@Bean
ExceptionMappingAuthenticationFailureHandler exceptionMappingAuthenticationFailureHandler(){
ExceptionMappingAuthenticationFailureHandler ex = new ExceptionMappingAuthenticationFailureHandler();
String RUTAERROR1 = "/login.zul?login_error=1";
String RUTAERROR2 = "/login.zul?login_error=2";
Map<String, String> errores = new HashMap<>();
errores.put(org.springframework.security.authentication.ProviderNotFoundException.class.getCanonicalName(), RUTAERROR1);
errores.put(SessionAuthenticationException.class.getCanonicalName(), RUTAERROR2);
ex.setExceptionMappings(errores);
return ex;
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/zkau/**").permitAll()
.antMatchers("/css/**").permitAll()
.antMatchers("/services/**").permitAll()
.antMatchers("/images/**").permitAll()
.antMatchers("/forgotpassword.zul").permitAll()
.antMatchers("/login.zul**").permitAll()
.antMatchers("/**").authenticated()
.and()
.formLogin()
.loginProcessingUrl("/j_spring_security_check")
.successHandler(customSuccessHandler())
.loginPage("/login.zul")
.permitAll()
// .defaultSuccessUrl("/index.zul")
// .defaultSuccessUrl("/menuAgrupadores.zul").
.failureHandler(exceptionMappingAuthenticationFailureHandler())
// .failureUrl("/login.zul?login_error=1")
.and()
.logout()
// .logoutSuccessUrl("/login.zul")
.logoutUrl("/j_spring_security_logout")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.logoutSuccessHandler(logoutSuccessHandler())
.and().csrf().disable();
http.headers()
.frameOptions().sameOrigin()
.httpStrictTransportSecurity().disable();
http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
}
SuccessHandler
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Resource
private UsuariosRepository usuariosRepository;
private RedirectStrategy redirect = new DefaultRedirectStrategy();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth)
throws IOException, ServletException {
Usuarios user = usuariosRepository.findByUsuario(auth.getName());
if(user.getToken() != null) {
throw new SessionLimitException("Max sessions limit reached!", new ProviderNotFoundException("Máximo de sesiones excedido.")); //This is what I'm trying to do
} else {
user.setToken(request.getSession().getId());
usuariosRepository.save(user);
redirect.sendRedirect(request, response, "/menuAgrupadores.zul");
}
}
SessionLimitException只是扩展了AuthenticationException而没有进一步的逻辑。
编辑
正如@Eleftheria Stein-Kousathana所说,我缺少了侦听器,但必须将其添加到web.xml中。就文档而言,这是显而易见的,但是我的应用程序没有此文件开头,并且所有其他bean / config都正常运行。换句话说,HttpSessionEventPublisher的@Bean表示法在我的实例中不起作用。如果您遇到相同的问题,强烈建议您尝试使用@Bean和XML方式来注册此侦听器。如果您不知道,应将web.xml放在WEB-INF中。
工作代码:
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/zkau/**").permitAll()
.antMatchers("/css/**").permitAll()
.antMatchers("/services/**").permitAll()
.antMatchers("/images/**").permitAll()
.antMatchers("/forgotpassword.zul").permitAll()
.antMatchers("/login.zul**").permitAll()
.antMatchers("/**").authenticated()
.and()
.formLogin()
.loginProcessingUrl("/j_spring_security_check")
.loginPage("/login.zul")
.permitAll()
.defaultSuccessUrl("/menuAgrupadores.zul")
.failureHandler(exceptionMappingAuthenticationFailureHandler())
.and()
.logout()
.logoutUrl("/j_spring_security_logout")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.logoutSuccessUrl("/login.zul")
.and()
.csrf()
.disable();
http.headers()
.frameOptions()
.sameOrigin()
.httpStrictTransportSecurity()
.disable();
http.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry()); //Added this for good measure
HttpSessionEventPublisher的xml表示法
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
我建议坚持第一种方法。
您唯一缺少的是一个HttpSessionEventPublisher
。
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
添加此选项后,它将解决注销问题,并允许新用户登录。
没有这个,HttpSessionDestroyedEvent
注销时就不会将其发布到应用程序上下文中,也不会清除过期的会话。
这就是为什么不允许新用户登录。
请注意,在HttpSecurity.sessionManagement
Javadoc中声明:
使用SessionManagementConfigurer.maximumSessions(int)时,请不要忘记为应用程序配置HttpSessionEventPublisher,以确保清除过期的会话。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句