From 240e5d85cc838c6bee6508a4494ac79fdf210402 Mon Sep 17 00:00:00 2001 From: guqing Date: Fri, 2 Jun 2023 15:44:28 +0800 Subject: [PATCH 1/4] feat: supports redirecting to a specified page after successful login --- .../run/halo/oauth/Oauth2Authenticator.java | 71 ++++++++++++------ .../halo/oauth/Oauth2LoginConfiguration.java | 75 ++++++++++++++++--- 2 files changed, 115 insertions(+), 31 deletions(-) diff --git a/src/main/java/run/halo/oauth/Oauth2Authenticator.java b/src/main/java/run/halo/oauth/Oauth2Authenticator.java index bfd5638..81ce8db 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,12 +132,15 @@ 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, authentication, + bindingRedirectUri) ); } return userConnectionService.isConnected(registrationId, @@ -146,7 +151,7 @@ protected Mono onAuthenticationSuccess(Authentication authentication, return mappedToSystemUserAuthentication(registrationId, authenticationResult) .flatMap(result -> handleAuthenticationSuccess(result, - webFilterExchange)); + webFilterExchange, loginRedirectUri)); } // signup OAuth2User principal = authenticationResult.getPrincipal(); @@ -188,33 +193,55 @@ 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, + Authentication authentication, + String redirectUri) { + return getRedirectUri(webFilterExchange.getExchange(), redirectUri) + .defaultIfEmpty(URI.create("/console/dashboard")) + .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, authentication, + redirectUri) + ) .contextWrite( ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext))); } + Mono authenticationSuccessRedirection(WebFilterExchange webFilterExchange, + Authentication authentication, + String redirectUri) { + return getRedirectUri(webFilterExchange.getExchange(), redirectUri) + .defaultIfEmpty(URI.create("/console/dashboard")) + .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..7a51514 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"); } @@ -218,4 +218,61 @@ ReactiveOAuth2AuthorizedClientService getAuthorizedClientService() { getClientRegistrationRepository()); } } + + static class QueryParamRedirectServerAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler { + private static final String REDIRECT_URI_PARAM = "login_redirect_uri"; + private URI location = URI.create("/"); + + private final ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy(); + + /** + * Creates a new instance with location of "/" + */ + public QueryParamRedirectServerAuthenticationSuccessHandler() { + } + + /** + * Creates a new instance with the specified location + * @param location the location to redirect if the no request is cached in + * {@link #setRequestCache(ServerRequestCache)} + */ + public QueryParamRedirectServerAuthenticationSuccessHandler(String location) { + this.location = URI.create(location); + } + + @Override + public Mono onAuthenticationSuccess(WebFilterExchange webFilterExchange, + Authentication authentication) { + + return getRedirectUri(webFilterExchange.getExchange(), authentication) + .defaultIfEmpty(this.location) + .flatMap(redirectUri -> + redirectStrategy.sendRedirect(webFilterExchange.getExchange(), redirectUri) + ); + } + + Mono getRedirectUri(ServerWebExchange exchange, Authentication authentication) { + String redirectUriString = Optional.of(authentication) + .filter(a -> a instanceof OAuth2LoginAuthenticationToken) + .map(OAuth2LoginAuthenticationToken.class::cast) + .map(authenticationResult -> { + var additionalParameters = authenticationResult.getAuthorizationExchange() + .getAuthorizationRequest() + .getAdditionalParameters(); + return (String) additionalParameters.get(REDIRECT_URI_PARAM); + }) + .orElse(null); + 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); + } + } } From ddbd4917ac4ad74f1cf956d831678f1fb498157f Mon Sep 17 00:00:00 2001 From: guqing Date: Tue, 13 Jun 2023 11:07:10 +0800 Subject: [PATCH 2/4] refactor: remove unused lines --- .../run/halo/oauth/Oauth2Authenticator.java | 4 +- .../halo/oauth/Oauth2LoginConfiguration.java | 57 ------------------- 2 files changed, 2 insertions(+), 59 deletions(-) diff --git a/src/main/java/run/halo/oauth/Oauth2Authenticator.java b/src/main/java/run/halo/oauth/Oauth2Authenticator.java index 81ce8db..8ba1ec1 100644 --- a/src/main/java/run/halo/oauth/Oauth2Authenticator.java +++ b/src/main/java/run/halo/oauth/Oauth2Authenticator.java @@ -197,7 +197,7 @@ private Mono handleBindSuccessHandler(WebFilterExchange webFilterExchange, Authentication authentication, String redirectUri) { return getRedirectUri(webFilterExchange.getExchange(), redirectUri) - .defaultIfEmpty(URI.create("/console/dashboard")) + .defaultIfEmpty(URI.create("/console")) .flatMap( uri -> redirectStrategy.sendRedirect(webFilterExchange.getExchange(), uri)); } @@ -221,7 +221,7 @@ Mono authenticationSuccessRedirection(WebFilterExchange webFilterExchange, Authentication authentication, String redirectUri) { return getRedirectUri(webFilterExchange.getExchange(), redirectUri) - .defaultIfEmpty(URI.create("/console/dashboard")) + .defaultIfEmpty(URI.create("/console")) .flatMap(uri -> this.redirectStrategy.sendRedirect(webFilterExchange.getExchange(), uri) ) diff --git a/src/main/java/run/halo/oauth/Oauth2LoginConfiguration.java b/src/main/java/run/halo/oauth/Oauth2LoginConfiguration.java index 7a51514..163b061 100644 --- a/src/main/java/run/halo/oauth/Oauth2LoginConfiguration.java +++ b/src/main/java/run/halo/oauth/Oauth2LoginConfiguration.java @@ -218,61 +218,4 @@ ReactiveOAuth2AuthorizedClientService getAuthorizedClientService() { getClientRegistrationRepository()); } } - - static class QueryParamRedirectServerAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler { - private static final String REDIRECT_URI_PARAM = "login_redirect_uri"; - private URI location = URI.create("/"); - - private final ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy(); - - /** - * Creates a new instance with location of "/" - */ - public QueryParamRedirectServerAuthenticationSuccessHandler() { - } - - /** - * Creates a new instance with the specified location - * @param location the location to redirect if the no request is cached in - * {@link #setRequestCache(ServerRequestCache)} - */ - public QueryParamRedirectServerAuthenticationSuccessHandler(String location) { - this.location = URI.create(location); - } - - @Override - public Mono onAuthenticationSuccess(WebFilterExchange webFilterExchange, - Authentication authentication) { - - return getRedirectUri(webFilterExchange.getExchange(), authentication) - .defaultIfEmpty(this.location) - .flatMap(redirectUri -> - redirectStrategy.sendRedirect(webFilterExchange.getExchange(), redirectUri) - ); - } - - Mono getRedirectUri(ServerWebExchange exchange, Authentication authentication) { - String redirectUriString = Optional.of(authentication) - .filter(a -> a instanceof OAuth2LoginAuthenticationToken) - .map(OAuth2LoginAuthenticationToken.class::cast) - .map(authenticationResult -> { - var additionalParameters = authenticationResult.getAuthorizationExchange() - .getAuthorizationRequest() - .getAdditionalParameters(); - return (String) additionalParameters.get(REDIRECT_URI_PARAM); - }) - .orElse(null); - 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); - } - } } From eded1641cfbe41f457a74f93cb04facbc93bffb7 Mon Sep 17 00:00:00 2001 From: guqing Date: Mon, 3 Jul 2023 14:42:31 +0800 Subject: [PATCH 3/4] refactor: authentication success method --- src/main/java/run/halo/oauth/Oauth2Authenticator.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/run/halo/oauth/Oauth2Authenticator.java b/src/main/java/run/halo/oauth/Oauth2Authenticator.java index 8ba1ec1..6948631 100644 --- a/src/main/java/run/halo/oauth/Oauth2Authenticator.java +++ b/src/main/java/run/halo/oauth/Oauth2Authenticator.java @@ -210,7 +210,7 @@ Mono handleAuthenticationSuccess(Authentication authentication, SecurityContextImpl securityContext = new SecurityContextImpl(); securityContext.setAuthentication(authentication); return securityContextRepository.save(exchange, securityContext) - .then(authenticationSuccessRedirection(webFilterExchange, authentication, + .then(authenticationSuccessRedirection(webFilterExchange, redirectUri) ) .contextWrite( @@ -218,7 +218,6 @@ Mono handleAuthenticationSuccess(Authentication authentication, } Mono authenticationSuccessRedirection(WebFilterExchange webFilterExchange, - Authentication authentication, String redirectUri) { return getRedirectUri(webFilterExchange.getExchange(), redirectUri) .defaultIfEmpty(URI.create("/console")) From 1e8ae850669effa0c420b02cf22da4bbacb38ca8 Mon Sep 17 00:00:00 2001 From: guqing Date: Mon, 3 Jul 2023 15:23:38 +0800 Subject: [PATCH 4/4] refactor: bind success method --- src/main/java/run/halo/oauth/Oauth2Authenticator.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/run/halo/oauth/Oauth2Authenticator.java b/src/main/java/run/halo/oauth/Oauth2Authenticator.java index 6948631..c49a819 100644 --- a/src/main/java/run/halo/oauth/Oauth2Authenticator.java +++ b/src/main/java/run/halo/oauth/Oauth2Authenticator.java @@ -139,9 +139,7 @@ protected Mono onAuthenticationSuccess(Authentication authentication, if (Boolean.parseBoolean(socialConnection)) { // Social connect successfully, finish the process return createConnection(webFilterExchange, authenticationResult) - .then(handleBindSuccessHandler(webFilterExchange, authentication, - bindingRedirectUri) - ); + .then(handleBindSuccessHandler(webFilterExchange, bindingRedirectUri)); } return userConnectionService.isConnected(registrationId, authenticationResult.getName()) @@ -194,7 +192,6 @@ private Mono createConnection(WebFilterExchange webFilterExchange, } private Mono handleBindSuccessHandler(WebFilterExchange webFilterExchange, - Authentication authentication, String redirectUri) { return getRedirectUri(webFilterExchange.getExchange(), redirectUri) .defaultIfEmpty(URI.create("/console"))