inb4:一般来说,我对spring非常陌生,所以我知道处理springsecurity是一个艰难的过程。
热释光;dr:我可以从我的自定义successhandler向failurehandler“抛出异常”吗?
目前,我正在尝试实现一些行为与我们的登录页面。如果凭据错误,用户将被重定向回登录,并显示一条错误消息。如果所使用的凭据是正确的,但另一个人已经在使用该帐户,则用户将被重定向到登录,并显示一条消息错误:“max sessions error”。
到目前为止,将exceptionmappingauthenticationfailurehandler与springs内置的会话/并发控制一起使用可以正常工作,显示正确的错误消息,但是会话控制并没有像我预期的那样工作。这可能是我自己的配置不好,或者只是因为我不太理解文档(英语不是我的母语,所以我完全接受这种可能性)。我希望sessioncontrol的行为是,如果与给定用户的会话处于活动状态,则尝试登录的人将被踢回登录。目前它是这样做的,但是如果会话处于活动状态的用户注销并尝试重新登录,它将提示他/她以maxsessionsrerror登录。因为sessionmanagement.maximumsessions()限制了一个帐户的登录次数,而不是用户可以同时拥有的活动会话数。我可能错了,但我还在调查。我已经试着按照文档所说的设置我的配置,但是它没有像我期望的那样工作。
我目前的替代方法是,当我确认其他人当前正在使用帐户时,尝试在successhandler上引发异常,以便failurehandler可以将用户重定向到带有正确错误消息的登录页。我的理解是,如果我重定向到登录页面,会话不会被破坏。我可以试着自己毁掉它,但我诚实地相信spring会这么做,因为我缺乏经验,所以我提出了这个问题。
欢迎任何帮助或掌掴。我想学习,到目前为止我对安全有点疏忽。下面是config和successhandler的代码。小提示:这个项目没有使用web.xml,因为我不知道什么原因,所以我试着将大多数配置转换为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);
}
成功处理程序
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>
1条答案
按热度按时间i7uaboj41#
我建议坚持第一种方法。
你唯一缺少的就是一个
HttpSessionEventPublisher
.一旦你添加了这个,它将解决注销问题,一个新的用户将被允许登录。
如果没有这个
HttpSessionDestroyedEvent
未在注销时发布到应用程序上下文,并且未清理过期的会话。这就是为什么不允许新用户登录的原因。
通知
HttpSecurity.sessionManagement
javadoc声明:使用sessionmanagementconfigurer.maximumsessions(int)时,不要忘记为应用程序配置httpsessioneventpublisher,以确保清除过期的会话。