SpringSecurity系列——自定义登录退出成功处理day5-3(源于官网5.7.2版本)
退出登录
注入 HttpSecurity bean 时,会自动应用注销功能。 默认情况下,访问 URL /logout 将通过以下方式注销用户:
- 使 HTTP 会话无效
- 清理已配置的所有 RememberMe 身份验证
- 清除 SecurityContextHolder
- 重定向到 /login?logout
但是,与配置登录功能类似,您也有多种选项来进一步自定义您的注销要求:
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.logout(logout -> logout
.logoutUrl("/my/logout")
.logoutSuccessUrl("/my/index")
.logoutSuccessHandler(logoutSuccessHandler)
.invalidateHttpSession(true)
.addLogoutHandler(logoutHandler)
.deleteCookies(cookieNamesToClear)
)
...
}
- 提供注销支持。
- 触发注销的 URL(默认为 /logout)。 如果启用 CSRF 保护(默认),则请求也必须是 POST。 有关详细信息,请参阅 Javadoc。
- 注销后重定向到的 URL。 默认为 /login?logout。 有关详细信息,请参阅 Javadoc。
- 让我们指定一个自定义 LogoutSuccessHandler。 如果指定了此项,则忽略 logoutSuccessUrl()。 有关详细信息,请参阅 Javadoc。
- 指定注销时是否使 HttpSession 无效。 默认情况下是这样。 在幕后配置 SecurityContextLogoutHandler。 有关详细信息,请参阅 Javadoc。
- 添加一个 LogoutHandler。 SecurityContextLogoutHandler 默认添加为最后一个 LogoutHandler。
- 允许指定注销成功时要删除的 cookie 的名称。 这是显式添加 CookieClearingLogoutHandler 的快捷方式。
通常,为了自定义注销功能,您可以添加 LogoutHandler 和/或 LogoutSuccessHandler 实现。 对于许多常见的场景,这些处理程序在使用 fluent API 时被应用在幕后。
LogoutHandler(注销处理程序)
通常,LogoutHandler 实现表示能够参与注销处理的类。 预计将调用它们来执行必要的清理。 因此,他们不应该抛出异常。 提供了各种实现:
- PersistentTokenBasedRememberMeServices
- TokenBasedRememberMeServices
- CookieClearingLogoutHandler
- CsrfLogoutHandler
- SecurityContextLogoutHandler
- HeaderWriterLogoutHandler
有关详细信息,请参阅 Remember-Me 接口和实现。
除了直接提供 LogoutHandler 实现之外,fluent API 还提供了快捷方式,这些快捷方式在后台提供了相应的 LogoutHandler 实现。 例如,deleteCookies() 允许指定在注销成功时要删除的一个或多个 cookie 的名称。 与添加 CookieClearingLogoutHandler 相比,这是一种快捷方式。
LogoutSuccessHandler (注销成功处理程序)
LogoutSuccessHandler 在 LogoutFilter 成功注销后调用,以处理例如重定向或转发到适当的目的地。 请注意,该接口与 LogoutHandler 几乎相同,但可能会引发异常。
提供了以下实现:
- SimpleUrlLogoutSuccessHandler
- HttpStatusReturningLogoutSuccessHandler
如上所述,您不需要直接指定 SimpleUrlLogoutSuccessHandler。 相反,fluent API 通过设置 logoutSuccessUrl() 提供了一个快捷方式。 这将在幕后设置 SimpleUrlLogoutSuccessHandler。 注销后将重定向到提供的 URL。 默认为 /login?logout。
HttpStatusReturningLogoutSuccessHandler 在 REST API 类型的场景中可能很有趣。 此 LogoutSuccessHandler 允许您提供要返回的纯 HTTP 状态代码,而不是在成功注销时重定向到 URL。 如果未配置,则默认返回状态码 200。
自定义登录退出成功处理(实例)
解释
根据上方官方文档的解释我们在前后端分离的项目中,并不需要用户退出后让后端进行重定向,而是要让后端提供给前端一个json数据以确定退出成功 而且我们只要对LogoutSuccessHandler接口进行实现即可
1.在SpringSecurityConfig中进行配置
主要部分
//自定义logout处理
http.logout(
logout ->
logout.logoutSuccessHandler(new DefineLogoutSuccessHandler())
.invalidateHttpSession(true)
)
完整配置
package com.example.test1.config;
import com.example.test1.filter.LoginFilter;
import com.example.test1.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
@EnableWebSecurity
@Configuration
public class SpringSecurityConfig {
private final UserDetailsServiceImpl userDetailsService;
@Autowired
public SpringSecurityConfig(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
AuthenticationManager authenticationManager = configuration.getAuthenticationManager();
System.out.println(authenticationManager);
return configuration.getAuthenticationManager();
}
//身份验证事件,发布 AuthenticationEventPublisher
@Bean
public AuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
return new DefaultAuthenticationEventPublisher(applicationEventPublisher);
}
//认证事件
@Bean
public AuthorizationEventPublisher authorizationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
return new SpringAuthorizationEventPublisher(applicationEventPublisher);
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests(
auth -> auth.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults())
.logout(
logout ->
//自定义logout处理
logout.logoutSuccessHandler(new DefineLogoutSuccessHandler())
.invalidateHttpSession(true)
)
.userDetailsService(userDetailsService);
return http.build();
}
}
2.编写自定义退出成功处理实现LogoutSuccessHandler接口
package com.example.test1.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
public class DefineLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println("=============logout handler==============");
System.out.println(request.getMethod());
System.out.println(authentication.getPrincipal());
HashMap<String, Object> data = new HashMap<>();
data.put("status",200);
data.put("msg","退出登录成功");
data.put("userData",authentication.getPrincipal());
response.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(data);
response.getWriter().println(s);
}
}
3.测试