从 CustomLogoutHandler 到 'login?logout' 的重定向被重定向到 'login'
Redirect from CustomLogoutHandler to 'login?logout' is redirected to 'login'
我有两个安全区域,我为其提供基于表单的登录。
/user
/admin
以下按预期工作:
- 访问受保护资源时重定向到登录页面
- 凭据为假时使用
?error
参数重定向到登录页面
- 根据定义的角色阻止访问受保护的资源
以下无效:
- 注销后重定向到登录页面
注销时,我重定向到 /user/login?logout
以显示一条消息“您已注销”,表明注销成功。但是,这不起作用,而是将重定向到 /user/login?logout
重定向到 /user/login
,因此不会显示任何消息。
当我删除自定义注销处理程序 .logoutSuccessHandler(logoutSuccessHandler())
并改为包含 .logoutSuccessUrl("/user/login?logout").permitAll()
时,它起作用了!
但是我希望此处理程序在注销时执行其他操作。
@Configuration
@Order(1)
public static class FormLoginUser extends WebSecurityConfigurerAdapter {
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new UserCustomAccessDeniedHandler();
}
@Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new UserCustomLogoutSuccessHandler();
}
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new UserCustomAuthenticationSuccessHandler();
}
private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource() {
return WebAuthenticationDetails::new;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/user/**")
.authorizeRequests().anyRequest().hasRole("USER")
.and()
.formLogin()
.loginPage("/user/login")
.permitAll()
.authenticationDetailsSource(authenticationDetailsSource())
.successHandler(authenticationSuccessHandler())
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessUrl("/user/login?logout").permitAll()
.logoutSuccessHandler(logoutSuccessHandler())
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler())
;
}
}
@Configuration
@Order(2)
public static class FormLoginAdmin extends WebSecurityConfigurerAdapter {
@Bean
public AccessDeniedHandler adminAccessDeniedHandler() {
return new AdminCustomAccessDeniedHandler();
}
@Bean
public LogoutSuccessHandler adminLogoutSuccessHandler() {
return new AdminCustomLogoutSuccessHandler();
}
@Bean
public AuthenticationSuccessHandler adminAuthenticationSuccessHandler() {
return new AdminCustomAuthenticationSuccessHandler();
}
private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource() {
return WebAuthenticationDetails::new;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin/**")
.authorizeRequests().anyRequest().hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/admin/login")
.authenticationDetailsSource(authenticationDetailsSource())
.successHandler(adminAuthenticationSuccessHandler())
.permitAll()
.and()
.logout()
.logoutUrl("/admin/logout")
.logoutSuccessHandler(adminLogoutSuccessHandler())
.and()
.exceptionHandling().accessDeniedHandler(adminAccessDeniedHandler());
}
}
这是我的 CustomLogoutHandler:
public class UserCustomLogoutSuccessHandler extends
SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
this.setDefaultTargetUrl("/user/login?logout");
super.onLogoutSuccess(request, response, authentication);
}
}
在 DevTools 中,我可以清楚地看到,每当我尝试访问 URL /user/login?logout
时,GET-Request 结果为 302,然后为 user/login
发出新请求.当我在浏览器 URL 行中手动编辑 URL 或从应用程序通过 FORM-Post 触发注销时,就会发生这种情况。
当我删除 logoutSuccessHandler
时,我可以在浏览器中手动输入 URL 并在应用程序中从我的 FORM-Post 触发它。
我也试过:
- 将用于登录和注销的 URL 移出 路径 路径
/user
-> 破坏了登录
- 定义第三个配置
Order(1)
明确允许登录和注销页面上的 GET 和 POST -> 这也破坏了登录
- 不使用
.antMatcher
而是使用 .antMatchers
,但是我想我将无法拥有两个不同的 FormLogins
我知道这类问题会打一个大大的问号,让人挠头想WHY?
解决方案是。
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.antMatcher("/user/**").authorizeRequests()
.antMatchers("/user/login").permitAll() //solution
.anyRequest().hasRole("USER")
.and()
.formLogin()
.loginPage("/user/login")
.permitAll()
.authenticationDetailsSource(authenticationDetailsSource())
.successHandler(authenticationSuccessHandler())
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessUrl("/user/login?logout").permitAll()
.logoutSuccessHandler(logoutSuccessHandler())
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
}
想知道为什么吗?
从以前的配置
您的资源 /user/login
是受限资源,仅当
时才允许使用
authenticated = true
和 hasRole = "User"
在您的注销成功处理程序中,您使会话无效并重定向到 /user/login?logout
页面,但 /user/login
是受限资源,因此 FilterSecurityInterceptor
将重定向到配置的登录页面(.loginPage("/user/login")
。因此你将不会收到任何传入查询字符串的参数。
因此解决方案总是将登录页面设为不受限制的资源。
我有两个安全区域,我为其提供基于表单的登录。
/user
/admin
以下按预期工作:
- 访问受保护资源时重定向到登录页面
- 凭据为假时使用
?error
参数重定向到登录页面 - 根据定义的角色阻止访问受保护的资源
以下无效:
- 注销后重定向到登录页面
注销时,我重定向到 /user/login?logout
以显示一条消息“您已注销”,表明注销成功。但是,这不起作用,而是将重定向到 /user/login?logout
重定向到 /user/login
,因此不会显示任何消息。
当我删除自定义注销处理程序 .logoutSuccessHandler(logoutSuccessHandler())
并改为包含 .logoutSuccessUrl("/user/login?logout").permitAll()
时,它起作用了!
但是我希望此处理程序在注销时执行其他操作。
@Configuration
@Order(1)
public static class FormLoginUser extends WebSecurityConfigurerAdapter {
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new UserCustomAccessDeniedHandler();
}
@Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new UserCustomLogoutSuccessHandler();
}
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new UserCustomAuthenticationSuccessHandler();
}
private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource() {
return WebAuthenticationDetails::new;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/user/**")
.authorizeRequests().anyRequest().hasRole("USER")
.and()
.formLogin()
.loginPage("/user/login")
.permitAll()
.authenticationDetailsSource(authenticationDetailsSource())
.successHandler(authenticationSuccessHandler())
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessUrl("/user/login?logout").permitAll()
.logoutSuccessHandler(logoutSuccessHandler())
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler())
;
}
}
@Configuration
@Order(2)
public static class FormLoginAdmin extends WebSecurityConfigurerAdapter {
@Bean
public AccessDeniedHandler adminAccessDeniedHandler() {
return new AdminCustomAccessDeniedHandler();
}
@Bean
public LogoutSuccessHandler adminLogoutSuccessHandler() {
return new AdminCustomLogoutSuccessHandler();
}
@Bean
public AuthenticationSuccessHandler adminAuthenticationSuccessHandler() {
return new AdminCustomAuthenticationSuccessHandler();
}
private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource() {
return WebAuthenticationDetails::new;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin/**")
.authorizeRequests().anyRequest().hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/admin/login")
.authenticationDetailsSource(authenticationDetailsSource())
.successHandler(adminAuthenticationSuccessHandler())
.permitAll()
.and()
.logout()
.logoutUrl("/admin/logout")
.logoutSuccessHandler(adminLogoutSuccessHandler())
.and()
.exceptionHandling().accessDeniedHandler(adminAccessDeniedHandler());
}
}
这是我的 CustomLogoutHandler:
public class UserCustomLogoutSuccessHandler extends
SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
this.setDefaultTargetUrl("/user/login?logout");
super.onLogoutSuccess(request, response, authentication);
}
}
在 DevTools 中,我可以清楚地看到,每当我尝试访问 URL /user/login?logout
时,GET-Request 结果为 302,然后为 user/login
发出新请求.当我在浏览器 URL 行中手动编辑 URL 或从应用程序通过 FORM-Post 触发注销时,就会发生这种情况。
当我删除 logoutSuccessHandler
时,我可以在浏览器中手动输入 URL 并在应用程序中从我的 FORM-Post 触发它。
我也试过:
- 将用于登录和注销的 URL 移出 路径 路径
/user
-> 破坏了登录 - 定义第三个配置
Order(1)
明确允许登录和注销页面上的 GET 和 POST -> 这也破坏了登录 - 不使用
.antMatcher
而是使用.antMatchers
,但是我想我将无法拥有两个不同的 FormLogins
我知道这类问题会打一个大大的问号,让人挠头想WHY?
解决方案是。
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.antMatcher("/user/**").authorizeRequests()
.antMatchers("/user/login").permitAll() //solution
.anyRequest().hasRole("USER")
.and()
.formLogin()
.loginPage("/user/login")
.permitAll()
.authenticationDetailsSource(authenticationDetailsSource())
.successHandler(authenticationSuccessHandler())
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessUrl("/user/login?logout").permitAll()
.logoutSuccessHandler(logoutSuccessHandler())
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
}
想知道为什么吗?
从以前的配置您的资源 /user/login
是受限资源,仅当
时才允许使用
authenticated = true
和 hasRole = "User"
在您的注销成功处理程序中,您使会话无效并重定向到 /user/login?logout
页面,但 /user/login
是受限资源,因此 FilterSecurityInterceptor
将重定向到配置的登录页面(.loginPage("/user/login")
。因此你将不会收到任何传入查询字符串的参数。
因此解决方案总是将登录页面设为不受限制的资源。