diff --git a/application/src/main/java/run/halo/app/infra/config/WebServerSecurityConfig.java b/application/src/main/java/run/halo/app/infra/config/WebServerSecurityConfig.java index 3842cee30..4385ae974 100644 --- a/application/src/main/java/run/halo/app/infra/config/WebServerSecurityConfig.java +++ b/application/src/main/java/run/halo/app/infra/config/WebServerSecurityConfig.java @@ -44,6 +44,7 @@ import run.halo.app.security.authentication.impl.RsaKeyService; import run.halo.app.security.authentication.pat.PatAuthenticationManager; import run.halo.app.security.authentication.pat.PatServerWebExchangeMatcher; import run.halo.app.security.authorization.AuthorityUtils; +import run.halo.app.security.authorization.NotAuthenticatedAuthorizationManager; import run.halo.app.security.authorization.RequestInfoAuthorizationManager; import run.halo.app.security.session.InMemoryReactiveIndexedSessionRepository; import run.halo.app.security.session.ReactiveIndexedSessionRepository; @@ -90,6 +91,8 @@ public class WebServerSecurityConfig { "/apis/**", "/actuator/**" ).access(new RequestInfoAuthorizationManager(roleService)) + .pathMatchers(HttpMethod.GET, "/login", "/signup") + .access(new NotAuthenticatedAuthorizationManager()) .pathMatchers( "/login/**", "/challenges/**", diff --git a/application/src/main/java/run/halo/app/security/ExceptionSecurityConfigurer.java b/application/src/main/java/run/halo/app/security/ExceptionSecurityConfigurer.java index 8cf715d07..940b232e4 100644 --- a/application/src/main/java/run/halo/app/security/ExceptionSecurityConfigurer.java +++ b/application/src/main/java/run/halo/app/security/ExceptionSecurityConfigurer.java @@ -1,7 +1,11 @@ package run.halo.app.security; +import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.anyExchange; +import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers; + import java.util.ArrayList; import org.springframework.context.MessageSource; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.oauth2.server.resource.web.access.server.BearerTokenServerAccessDeniedHandler; @@ -11,8 +15,6 @@ import org.springframework.security.web.server.authentication.AuthenticationConv import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler; import org.springframework.security.web.server.authorization.ServerWebExchangeDelegatingServerAccessDeniedHandler; import org.springframework.security.web.server.savedrequest.ServerRequestCache; -import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; -import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.server.ServerResponse; import run.halo.app.security.authentication.SecurityConfigurer; @@ -40,7 +42,7 @@ public class ExceptionSecurityConfigurer implements SecurityConfigurer { http.exceptionHandling(exception -> { var accessDeniedHandlers = new ArrayList( - 2 + 3 ); accessDeniedHandlers.add( new ServerWebExchangeDelegatingServerAccessDeniedHandler.DelegateEntry( @@ -51,19 +53,24 @@ public class ExceptionSecurityConfigurer implements SecurityConfigurer { )); accessDeniedHandlers.add( new ServerWebExchangeDelegatingServerAccessDeniedHandler.DelegateEntry( - ServerWebExchangeMatchers.anyExchange(), + pathMatchers(HttpMethod.GET, "/login", "/signup"), + new RedirectAccessDeniedHandler("/uc") + )); + accessDeniedHandlers.add( + new ServerWebExchangeDelegatingServerAccessDeniedHandler.DelegateEntry( + anyExchange(), new HttpStatusServerAccessDeniedHandler(HttpStatus.FORBIDDEN) ) ); var entryPoints = - new ArrayList(3); + new ArrayList(2); entryPoints.add(new DelegatingServerAuthenticationEntryPoint.DelegateEntry( TwoFactorAuthenticationEntryPoint.MATCHER, new TwoFactorAuthenticationEntryPoint(messageSource, context) )); entryPoints.add(new DelegatingServerAuthenticationEntryPoint.DelegateEntry( - exchange -> ServerWebExchangeMatcher.MatchResult.match(), + anyExchange(), new DefaultServerAuthenticationEntryPoint(serverRequestCache) )); diff --git a/application/src/main/java/run/halo/app/security/RedirectAccessDeniedHandler.java b/application/src/main/java/run/halo/app/security/RedirectAccessDeniedHandler.java new file mode 100644 index 000000000..c75d37674 --- /dev/null +++ b/application/src/main/java/run/halo/app/security/RedirectAccessDeniedHandler.java @@ -0,0 +1,31 @@ +package run.halo.app.security; + +import java.net.URI; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.server.DefaultServerRedirectStrategy; +import org.springframework.security.web.server.ServerRedirectStrategy; +import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * Redirect access denied handler. + * + * @author johnniang + * @since 2.20.0 + */ +public class RedirectAccessDeniedHandler implements ServerAccessDeniedHandler { + + private final ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy(); + + private final URI redirectUri; + + public RedirectAccessDeniedHandler(String redirectUri) { + this.redirectUri = URI.create(redirectUri); + } + + @Override + public Mono handle(ServerWebExchange exchange, AccessDeniedException denied) { + return redirectStrategy.sendRedirect(exchange, redirectUri); + } +} diff --git a/application/src/main/java/run/halo/app/security/authorization/NotAuthenticatedAuthorizationManager.java b/application/src/main/java/run/halo/app/security/authorization/NotAuthenticatedAuthorizationManager.java new file mode 100644 index 000000000..c0435f5d0 --- /dev/null +++ b/application/src/main/java/run/halo/app/security/authorization/NotAuthenticatedAuthorizationManager.java @@ -0,0 +1,30 @@ +package run.halo.app.security.authorization; + +import org.springframework.security.authentication.AuthenticationTrustResolver; +import org.springframework.security.authentication.AuthenticationTrustResolverImpl; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.ReactiveAuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.server.authorization.AuthorizationContext; +import reactor.core.publisher.Mono; + +/** + * Authorization manager that checks if the user is not authenticated. + * + * @author johnniang + * @since 2.20.0 + */ +public class NotAuthenticatedAuthorizationManager + implements ReactiveAuthorizationManager { + + private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); + + @Override + public Mono check(Mono authentication, + AuthorizationContext object) { + return authentication.map(a -> !trustResolver.isAuthenticated(a)) + .defaultIfEmpty(true) + .map(AuthorizationDecision::new); + } + +}