Refactor logout handler (#7470)

#### What type of PR is this?

/kind cleanup
/area core
/milestone 2.21.x

#### What this PR does / why we need it:

This PR corrects location of LogoutHandler instead of in LogoutSuccessHandler. LogoutHanadler should be invoked before LogoutSuccessHandler.

#### Does this PR introduce a user-facing change?

```release-note
None
```
pull/7475/head
John Niang 2025-05-26 11:44:14 +08:00 committed by GitHub
parent 5c27a0484b
commit ccdb97743b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 25 additions and 23 deletions

View File

@ -3,6 +3,7 @@ package run.halo.app.security;
import static run.halo.app.security.authentication.WebExchangeMatchers.ignoringMediaTypeAll; import static run.halo.app.security.authentication.WebExchangeMatchers.ignoringMediaTypeAll;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -19,8 +20,10 @@ import org.springframework.security.web.server.DefaultServerRedirectStrategy;
import org.springframework.security.web.server.ServerRedirectStrategy; import org.springframework.security.web.server.ServerRedirectStrategy;
import org.springframework.security.web.server.WebFilterExchange; import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler; import org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler;
import org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler;
import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler; import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;
import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler; import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.security.web.server.savedrequest.ServerRequestCache; import org.springframework.security.web.server.savedrequest.ServerRequestCache;
import org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache; import org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
@ -34,15 +37,12 @@ import run.halo.app.core.user.service.UserLoginOrLogoutProcessing;
import run.halo.app.core.user.service.UserService; import run.halo.app.core.user.service.UserService;
import run.halo.app.infra.actuator.GlobalInfoService; import run.halo.app.infra.actuator.GlobalInfoService;
import run.halo.app.security.authentication.SecurityConfigurer; import run.halo.app.security.authentication.SecurityConfigurer;
import run.halo.app.security.authentication.rememberme.RememberMeServices;
import run.halo.app.theme.router.ModelConst; import run.halo.app.theme.router.ModelConst;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@Order(0) @Order(0)
public class LogoutSecurityConfigurer implements SecurityConfigurer { class LogoutSecurityConfigurer implements SecurityConfigurer {
private final RememberMeServices rememberMeServices;
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
@ -50,14 +50,29 @@ public class LogoutSecurityConfigurer implements SecurityConfigurer {
private final ServerRequestCache serverRequestCache = new HaloServerRequestCache(); private final ServerRequestCache serverRequestCache = new HaloServerRequestCache();
private final ServerSecurityContextRepository securityContextRepository;
@Override @Override
public void configure(ServerHttpSecurity http) { public void configure(ServerHttpSecurity http) {
var serverLogoutHandlers = getLogoutHandlers(); http.logout(logout -> logout
http.logout( .logoutHandler(getLogoutHandler())
logout -> logout.logoutSuccessHandler(new LogoutSuccessHandler(serverLogoutHandlers)) .logoutSuccessHandler(new LogoutSuccessHandler())
); );
} }
private ServerLogoutHandler getLogoutHandler() {
var defaultLogoutHandler = new SecurityContextServerLogoutHandler();
defaultLogoutHandler.setSecurityContextRepository(securityContextRepository);
var logoutHandlers = new ArrayList<ServerLogoutHandler>();
logoutHandlers.add(defaultLogoutHandler);
applicationContext.getBeanProvider(ServerLogoutHandler.class)
.forEach(logoutHandlers::add);
if (logoutHandlers.size() == 1) {
return logoutHandlers.getFirst();
}
return new DelegatingServerLogoutHandler(logoutHandlers);
}
@Bean @Bean
RouterFunction<ServerResponse> logoutPage( RouterFunction<ServerResponse> logoutPage(
UserService userService, UserService userService,
@ -93,25 +108,17 @@ public class LogoutSecurityConfigurer implements SecurityConfigurer {
private class LogoutSuccessHandler implements ServerLogoutSuccessHandler { private class LogoutSuccessHandler implements ServerLogoutSuccessHandler {
private final ServerLogoutSuccessHandler defaultHandler; private final ServerLogoutSuccessHandler defaultHandler;
private final ServerLogoutHandler logoutHandler;
public LogoutSuccessHandler(ServerLogoutHandler... logoutHandlers) { public LogoutSuccessHandler() {
var redirectHandler = new RequestCacheRedirectLogoutSuccessHandler(); var redirectHandler = new RequestCacheRedirectLogoutSuccessHandler();
redirectHandler.setRequestCache(serverRequestCache); redirectHandler.setRequestCache(serverRequestCache);
this.defaultHandler = redirectHandler; this.defaultHandler = redirectHandler;
if (logoutHandlers.length == 1) {
this.logoutHandler = logoutHandlers[0];
} else {
this.logoutHandler = new DelegatingServerLogoutHandler(logoutHandlers);
}
} }
@Override @Override
public Mono<Void> onLogoutSuccess(WebFilterExchange exchange, public Mono<Void> onLogoutSuccess(WebFilterExchange exchange,
Authentication authentication) { Authentication authentication) {
return logoutHandler.logout(exchange, authentication) return userLoginOrLogoutProcessing.logoutProcessing(authentication.getName())
.then(rememberMeServices.loginFail(exchange.getExchange()))
.then(userLoginOrLogoutProcessing.logoutProcessing(authentication.getName()))
.then(ignoringMediaTypeAll(MediaType.APPLICATION_JSON) .then(ignoringMediaTypeAll(MediaType.APPLICATION_JSON)
.matches(exchange.getExchange()) .matches(exchange.getExchange())
.filter(ServerWebExchangeMatcher.MatchResult::isMatch) .filter(ServerWebExchangeMatcher.MatchResult::isMatch)
@ -127,11 +134,6 @@ public class LogoutSecurityConfigurer implements SecurityConfigurer {
} }
} }
private ServerLogoutHandler[] getLogoutHandlers() {
return applicationContext.getBeansOfType(ServerLogoutHandler.class).values()
.toArray(new ServerLogoutHandler[0]);
}
private static class RequestCacheRedirectLogoutSuccessHandler private static class RequestCacheRedirectLogoutSuccessHandler
implements ServerLogoutSuccessHandler { implements ServerLogoutSuccessHandler {

View File

@ -367,7 +367,7 @@ public class TokenBasedRememberMeServices implements ServerLogoutHandler, Rememb
log.debug("Logout of user {}", (authentication != null) ? authentication.getName() log.debug("Logout of user {}", (authentication != null) ? authentication.getName()
: "Unknown"); : "Unknown");
} }
return onLogout(exchange, authentication); return loginFail(exchange.getExchange()).then(onLogout(exchange, authentication));
} }
protected Mono<Void> onLogout(WebFilterExchange exchange, Authentication authentication) { protected Mono<Void> onLogout(WebFilterExchange exchange, Authentication authentication) {