From 3a4bf91b10336baeb4e9cd11bc1b8f71a56899fd Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Mon, 3 Jul 2023 15:44:12 +0800 Subject: [PATCH] feat: supports redirecting to a specified page after successful login (#33) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What this PR does? 登录成功后支持跳转到指定页面 /kind feature Fixes #23 ```release-note 登录成功后支持跳转到指定页面 ``` --- .../run/halo/oauth/Oauth2Authenticator.java | 69 ++++++++++++------- .../halo/oauth/Oauth2LoginConfiguration.java | 18 ++--- 2 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/main/java/run/halo/oauth/Oauth2Authenticator.java b/src/main/java/run/halo/oauth/Oauth2Authenticator.java index bfd5638..c49a819 100644 --- a/src/main/java/run/halo/oauth/Oauth2Authenticator.java +++ b/src/main/java/run/halo/oauth/Oauth2Authenticator.java @@ -3,8 +3,10 @@ import static org.apache.commons.lang3.StringUtils.defaultString; import static run.halo.oauth.SocialServerOauth2AuthorizationRequestResolver.SOCIAL_CONNECTION; +import java.net.URI; import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.lang.NonNull; import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -18,6 +20,8 @@ import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter; import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.security.web.server.DefaultServerRedirectStrategy; +import org.springframework.security.web.server.ServerRedirectStrategy; import org.springframework.security.web.server.WebFilterExchange; import org.springframework.security.web.server.authentication.AuthenticationWebFilter; import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler; @@ -84,8 +88,6 @@ AuthenticationWebFilter createAuthenticationWebFilter() { oauth2LoginConfiguration.getAuthenticationMatcher()); authenticationFilter.setServerAuthenticationConverter( oauth2LoginConfiguration.getAuthenticationConverter()); - authenticationFilter.setAuthenticationSuccessHandler( - oauth2LoginConfiguration.getAuthenticationSuccessHandler()); authenticationFilter.setAuthenticationFailureHandler( oauth2LoginConfiguration.getAuthenticationFailureHandler()); authenticationFilter.setSecurityContextRepository(this.securityContextRepository); @@ -94,7 +96,7 @@ AuthenticationWebFilter createAuthenticationWebFilter() { class SocialLoginAuthenticationWebFilter extends OAuth2LoginAuthenticationWebFilter { - private ServerAuthenticationSuccessHandler authenticationSuccessHandler; + private final ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy(); private final ServerOAuth2AuthorizedClientRepository authorizedClientRepository; /** @@ -130,13 +132,14 @@ protected Mono onAuthenticationSuccess(Authentication authentication, .getAuthorizationRequest() .getAdditionalParameters(); String socialConnection = (String) additionalParameters.get(SOCIAL_CONNECTION); - String redirectUri = (String) additionalParameters.get("binding_redirect_uri"); + String bindingRedirectUri = + (String) additionalParameters.get("binding_redirect_uri"); + String loginRedirectUri = + (String) additionalParameters.get("login_redirect_uri"); if (Boolean.parseBoolean(socialConnection)) { // Social connect successfully, finish the process return createConnection(webFilterExchange, authenticationResult) - .then(bindSuccessHandler(redirectUri) - .onAuthenticationSuccess(webFilterExchange, authentication) - ); + .then(handleBindSuccessHandler(webFilterExchange, bindingRedirectUri)); } return userConnectionService.isConnected(registrationId, authenticationResult.getName()) @@ -146,7 +149,7 @@ protected Mono onAuthenticationSuccess(Authentication authentication, return mappedToSystemUserAuthentication(registrationId, authenticationResult) .flatMap(result -> handleAuthenticationSuccess(result, - webFilterExchange)); + webFilterExchange, loginRedirectUri)); } // signup OAuth2User principal = authenticationResult.getPrincipal(); @@ -188,33 +191,53 @@ private Mono createConnection(WebFilterExchange webFilterExchange, .then(); } - private ServerAuthenticationSuccessHandler bindSuccessHandler(String redirectUri) { - if (StringUtils.isBlank(redirectUri)) { - return new RedirectServerAuthenticationSuccessHandler("/console/dashboard"); - } - return new RedirectServerAuthenticationSuccessHandler(redirectUri); - } - - @Override - public void setAuthenticationSuccessHandler( - ServerAuthenticationSuccessHandler authenticationSuccessHandler) { - super.setAuthenticationSuccessHandler(authenticationSuccessHandler); - this.authenticationSuccessHandler = authenticationSuccessHandler; + private Mono handleBindSuccessHandler(WebFilterExchange webFilterExchange, + String redirectUri) { + return getRedirectUri(webFilterExchange.getExchange(), redirectUri) + .defaultIfEmpty(URI.create("/console")) + .flatMap( + uri -> redirectStrategy.sendRedirect(webFilterExchange.getExchange(), uri)); } Mono handleAuthenticationSuccess(Authentication authentication, - WebFilterExchange webFilterExchange) { + WebFilterExchange webFilterExchange, + String redirectUri) { // Save the authentication result in the SecurityContext ServerWebExchange exchange = webFilterExchange.getExchange(); SecurityContextImpl securityContext = new SecurityContextImpl(); securityContext.setAuthentication(authentication); return securityContextRepository.save(exchange, securityContext) - .then(this.authenticationSuccessHandler.onAuthenticationSuccess(webFilterExchange, - authentication)) + .then(authenticationSuccessRedirection(webFilterExchange, + redirectUri) + ) .contextWrite( ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext))); } + Mono authenticationSuccessRedirection(WebFilterExchange webFilterExchange, + String redirectUri) { + return getRedirectUri(webFilterExchange.getExchange(), redirectUri) + .defaultIfEmpty(URI.create("/console")) + .flatMap(uri -> + this.redirectStrategy.sendRedirect(webFilterExchange.getExchange(), uri) + ) + .then(); + } + + Mono getRedirectUri(ServerWebExchange exchange, String redirectUriString) { + ServerHttpRequest request = exchange.getRequest(); + if (StringUtils.isBlank(redirectUriString)) { + return Mono.empty(); + } + URI redirectUri = URI.create(redirectUriString); + // Only redirect to the same host and port + if (redirectUri.getAuthority() != null + && !redirectUri.getAuthority().equals(request.getURI().getAuthority())) { + return Mono.empty(); + } + return Mono.just(redirectUri); + } + Mono mappedToSystemUserAuthentication(String registrationId, Authentication authentication) { return socialUserDetailsService.loadUserByUserId(registrationId, diff --git a/src/main/java/run/halo/oauth/Oauth2LoginConfiguration.java b/src/main/java/run/halo/oauth/Oauth2LoginConfiguration.java index 46fa0ae..163b061 100644 --- a/src/main/java/run/halo/oauth/Oauth2LoginConfiguration.java +++ b/src/main/java/run/halo/oauth/Oauth2LoginConfiguration.java @@ -1,12 +1,18 @@ package run.halo.oauth; +import java.net.URI; +import java.util.Optional; import lombok.Getter; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager; import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2LoginReactiveAuthenticationManager; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; import org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient; @@ -34,6 +40,7 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory; import org.springframework.security.web.server.DefaultServerRedirectStrategy; import org.springframework.security.web.server.ServerRedirectStrategy; +import org.springframework.security.web.server.WebFilterExchange; import org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler; import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler; import org.springframework.security.web.server.authentication.ServerAuthenticationConverter; @@ -45,6 +52,8 @@ import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; import org.springframework.stereotype.Component; import org.springframework.util.ClassUtils; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; import run.halo.app.extension.ReactiveExtensionClient; /** @@ -57,7 +66,6 @@ @Component public final class Oauth2LoginConfiguration { private final ReactiveAuthenticationManager authenticationManager; - private final ServerAuthenticationSuccessHandler authenticationSuccessHandler; private final ServerAuthenticationFailureHandler authenticationFailureHandler; private final ServerWebExchangeMatcher authenticationMatcher; private final ServerOAuth2AuthorizedClientRepository authorizedClientRepository; @@ -81,7 +89,6 @@ public Oauth2LoginConfiguration(ReactiveExtensionClient extensionClient) { Initializer initializer = new Initializer(); this.authenticationManager = initializer.getAuthenticationManager(); - this.authenticationSuccessHandler = initializer.getAuthenticationSuccessHandler(); this.authenticationFailureHandler = initializer.getAuthenticationFailureHandler(); this.authenticationMatcher = initializer.getAuthenticationMatcher(); this.authorizedClientRepository = initializer.getAuthorizedClientRepository(); @@ -104,13 +111,6 @@ public void setRequestCache(ServerRequestCache requestCache) { class Initializer { - ServerAuthenticationSuccessHandler getAuthenticationSuccessHandler() { - RedirectServerAuthenticationSuccessHandler handler = - new RedirectServerAuthenticationSuccessHandler("/console/dashboard"); - handler.setRequestCache(requestCache); - return handler; - } - ServerAuthenticationFailureHandler getAuthenticationFailureHandler() { return new RedirectServerAuthenticationFailureHandler("/console/login?error"); }