mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-10-22 18:28:51 +00:00
Add AuthorizationManagerFactory
Signed-off-by: Steve Riesenberg <5248162+sjohnr@users.noreply.github.com>
This commit is contained in:
parent
a4f813ab29
commit
eeb4574bb3
@ -39,6 +39,7 @@ import org.springframework.security.aot.hint.PrePostAuthorizeHintsRegistrar;
|
|||||||
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
|
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
|
||||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
|
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
|
||||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
@ -121,6 +122,11 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, Applicati
|
|||||||
this.expressionHandler.setRoleHierarchy(roleHierarchy);
|
this.expressionHandler.setRoleHierarchy(roleHierarchy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
void setAuthorizationManagerFactory(AuthorizationManagerFactory<MethodInvocation> authorizationManagerFactory) {
|
||||||
|
this.expressionHandler.setAuthorizationManagerFactory(authorizationManagerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
|
void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
|
||||||
this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
||||||
|
@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.web.configurers;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
@ -27,13 +26,12 @@ import org.springframework.context.ApplicationContext;
|
|||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
|
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
|
||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
|
||||||
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
import org.springframework.security.authorization.AuthorizationManagers;
|
import org.springframework.security.authorization.AuthorizationManagers;
|
||||||
import org.springframework.security.authorization.SingleResultAuthorizationManager;
|
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||||
import org.springframework.security.config.ObjectPostProcessor;
|
import org.springframework.security.config.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||||
@ -46,13 +44,13 @@ import org.springframework.security.web.access.intercept.RequestMatcherDelegatin
|
|||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.function.SingletonSupplier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a URL based authorization using {@link AuthorizationManager}.
|
* Adds a URL based authorization using {@link AuthorizationManager}.
|
||||||
*
|
*
|
||||||
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured.
|
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured.
|
||||||
* @author Evgeniy Cheban
|
* @author Evgeniy Cheban
|
||||||
|
* @author Steve Riesenberg
|
||||||
* @since 5.5
|
* @since 5.5
|
||||||
*/
|
*/
|
||||||
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
|
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
@ -62,9 +60,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
|
|
||||||
private final AuthorizationEventPublisher publisher;
|
private final AuthorizationEventPublisher publisher;
|
||||||
|
|
||||||
private final Supplier<RoleHierarchy> roleHierarchy;
|
private final AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory;
|
||||||
|
|
||||||
private String rolePrefix = "ROLE_";
|
|
||||||
|
|
||||||
private ObjectPostProcessor<AuthorizationManager<HttpServletRequest>> postProcessor = ObjectPostProcessor
|
private ObjectPostProcessor<AuthorizationManager<HttpServletRequest>> postProcessor = ObjectPostProcessor
|
||||||
.identity();
|
.identity();
|
||||||
@ -81,13 +77,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
else {
|
else {
|
||||||
this.publisher = new SpringAuthorizationEventPublisher(context);
|
this.publisher = new SpringAuthorizationEventPublisher(context);
|
||||||
}
|
}
|
||||||
this.roleHierarchy = SingletonSupplier.of(() -> (context.getBeanNamesForType(RoleHierarchy.class).length > 0)
|
this.authorizationManagerFactory = getAuthorizationManagerFactory(context);
|
||||||
? context.getBean(RoleHierarchy.class) : new NullRoleHierarchy());
|
|
||||||
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
|
|
||||||
if (grantedAuthorityDefaultsBeanNames.length > 0) {
|
|
||||||
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(GrantedAuthorityDefaults.class);
|
|
||||||
this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
|
|
||||||
}
|
|
||||||
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,
|
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,
|
||||||
ResolvableType.forClassWithGenerics(AuthorizationManager.class, HttpServletRequest.class));
|
ResolvableType.forClassWithGenerics(AuthorizationManager.class, HttpServletRequest.class));
|
||||||
ObjectProvider<ObjectPostProcessor<AuthorizationManager<HttpServletRequest>>> provider = context
|
ObjectProvider<ObjectPostProcessor<AuthorizationManager<HttpServletRequest>>> provider = context
|
||||||
@ -95,6 +85,35 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
provider.ifUnique((postProcessor) -> this.postProcessor = postProcessor);
|
provider.ifUnique((postProcessor) -> this.postProcessor = postProcessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AuthorizationManagerFactory<RequestAuthorizationContext> getAuthorizationManagerFactory(
|
||||||
|
ApplicationContext context) {
|
||||||
|
ResolvableType authorizationManagerFactoryType = ResolvableType
|
||||||
|
.forClassWithGenerics(AuthorizationManagerFactory.class, RequestAuthorizationContext.class);
|
||||||
|
|
||||||
|
// Handle fallback to generic type
|
||||||
|
if (context.getBeanNamesForType(authorizationManagerFactoryType).length == 0) {
|
||||||
|
authorizationManagerFactoryType = ResolvableType.forClassWithGenerics(AuthorizationManagerFactory.class,
|
||||||
|
Object.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectProvider<AuthorizationManagerFactory<RequestAuthorizationContext>> authorizationManagerFactoryProvider = context
|
||||||
|
.getBeanProvider(authorizationManagerFactoryType);
|
||||||
|
|
||||||
|
return authorizationManagerFactoryProvider.getIfAvailable(() -> {
|
||||||
|
RoleHierarchy roleHierarchy = context.getBeanProvider(RoleHierarchy.class)
|
||||||
|
.getIfAvailable(NullRoleHierarchy::new);
|
||||||
|
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBeanProvider(GrantedAuthorityDefaults.class)
|
||||||
|
.getIfAvailable();
|
||||||
|
String rolePrefix = (grantedAuthorityDefaults != null) ? grantedAuthorityDefaults.getRolePrefix() : "ROLE_";
|
||||||
|
|
||||||
|
DefaultAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
authorizationManagerFactory.setRoleHierarchy(roleHierarchy);
|
||||||
|
authorizationManagerFactory.setRolePrefix(rolePrefix);
|
||||||
|
|
||||||
|
return authorizationManagerFactory;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link AuthorizationManagerRequestMatcherRegistry} is what users will interact
|
* The {@link AuthorizationManagerRequestMatcherRegistry} is what users will interact
|
||||||
* with after applying the {@link AuthorizeHttpRequestsConfigurer}.
|
* with after applying the {@link AuthorizeHttpRequestsConfigurer}.
|
||||||
@ -173,7 +192,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
@Override
|
@Override
|
||||||
protected AuthorizedUrl chainRequestMatchers(List<RequestMatcher> requestMatchers) {
|
protected AuthorizedUrl chainRequestMatchers(List<RequestMatcher> requestMatchers) {
|
||||||
this.unmappedMatchers = requestMatchers;
|
this.unmappedMatchers = requestMatchers;
|
||||||
return new AuthorizedUrl(requestMatchers);
|
return new AuthorizedUrl(requestMatchers, AuthorizeHttpRequestsConfigurer.this.authorizationManagerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -201,20 +220,31 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
|
|
||||||
private final List<? extends RequestMatcher> matchers;
|
private final List<? extends RequestMatcher> matchers;
|
||||||
|
|
||||||
|
private AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory;
|
||||||
|
|
||||||
private boolean not;
|
private boolean not;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
* @param matchers the {@link RequestMatcher} instances to map
|
* @param matchers the {@link RequestMatcher} instances to map
|
||||||
|
* @param authorizationManagerFactory the {@link AuthorizationManagerFactory} for
|
||||||
|
* creating instances of {@link AuthorizationManager}
|
||||||
*/
|
*/
|
||||||
AuthorizedUrl(List<? extends RequestMatcher> matchers) {
|
AuthorizedUrl(List<? extends RequestMatcher> matchers,
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory) {
|
||||||
this.matchers = matchers;
|
this.matchers = matchers;
|
||||||
|
this.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<? extends RequestMatcher> getMatchers() {
|
protected List<? extends RequestMatcher> getMatchers() {
|
||||||
return this.matchers;
|
return this.matchers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAuthorizationManagerFactory(
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory) {
|
||||||
|
this.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Negates the following authorization rule.
|
* Negates the following authorization rule.
|
||||||
* @return the {@link AuthorizedUrl} for further customization
|
* @return the {@link AuthorizedUrl} for further customization
|
||||||
@ -231,7 +261,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
* customizations
|
* customizations
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry permitAll() {
|
public AuthorizationManagerRequestMatcherRegistry permitAll() {
|
||||||
return access(SingleResultAuthorizationManager.permitAll());
|
return access(this.authorizationManagerFactory.permitAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -240,7 +270,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
* customizations
|
* customizations
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry denyAll() {
|
public AuthorizationManagerRequestMatcherRegistry denyAll() {
|
||||||
return access(SingleResultAuthorizationManager.denyAll());
|
return access(this.authorizationManagerFactory.denyAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -251,8 +281,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
* customizations
|
* customizations
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
|
public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
|
||||||
return access(withRoleHierarchy(AuthorityAuthorizationManager
|
return access(this.authorizationManagerFactory.hasRole(role));
|
||||||
.hasAnyRole(AuthorizeHttpRequestsConfigurer.this.rolePrefix, new String[] { role })));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -264,8 +293,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
* customizations
|
* customizations
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry hasAnyRole(String... roles) {
|
public AuthorizationManagerRequestMatcherRegistry hasAnyRole(String... roles) {
|
||||||
return access(withRoleHierarchy(
|
return access(this.authorizationManagerFactory.hasAnyRole(roles));
|
||||||
AuthorityAuthorizationManager.hasAnyRole(AuthorizeHttpRequestsConfigurer.this.rolePrefix, roles)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -275,7 +303,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
* customizations
|
* customizations
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {
|
public AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {
|
||||||
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority)));
|
return access(this.authorizationManagerFactory.hasAuthority(authority));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,13 +314,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
* customizations
|
* customizations
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry hasAnyAuthority(String... authorities) {
|
public AuthorizationManagerRequestMatcherRegistry hasAnyAuthority(String... authorities) {
|
||||||
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities)));
|
return access(this.authorizationManagerFactory.hasAnyAuthority(authorities));
|
||||||
}
|
|
||||||
|
|
||||||
private AuthorityAuthorizationManager<RequestAuthorizationContext> withRoleHierarchy(
|
|
||||||
AuthorityAuthorizationManager<RequestAuthorizationContext> manager) {
|
|
||||||
manager.setRoleHierarchy(AuthorizeHttpRequestsConfigurer.this.roleHierarchy.get());
|
|
||||||
return manager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -301,7 +323,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
* customizations
|
* customizations
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry authenticated() {
|
public AuthorizationManagerRequestMatcherRegistry authenticated() {
|
||||||
return access(AuthenticatedAuthorizationManager.authenticated());
|
return access(this.authorizationManagerFactory.authenticated());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -313,7 +335,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
* @see RememberMeConfigurer
|
* @see RememberMeConfigurer
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry fullyAuthenticated() {
|
public AuthorizationManagerRequestMatcherRegistry fullyAuthenticated() {
|
||||||
return access(AuthenticatedAuthorizationManager.fullyAuthenticated());
|
return access(this.authorizationManagerFactory.fullyAuthenticated());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -324,7 +346,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
* @see RememberMeConfigurer
|
* @see RememberMeConfigurer
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry rememberMe() {
|
public AuthorizationManagerRequestMatcherRegistry rememberMe() {
|
||||||
return access(AuthenticatedAuthorizationManager.rememberMe());
|
return access(this.authorizationManagerFactory.rememberMe());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -334,7 +356,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
|||||||
* @since 5.8
|
* @since 5.8
|
||||||
*/
|
*/
|
||||||
public AuthorizationManagerRequestMatcherRegistry anonymous() {
|
public AuthorizationManagerRequestMatcherRegistry anonymous() {
|
||||||
return access(AuthenticatedAuthorizationManager.anonymous());
|
return access(this.authorizationManagerFactory.anonymous());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.annotation.web.configurers;
|
package org.springframework.security.config.annotation.web.configurers;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import io.micrometer.observation.Observation;
|
import io.micrometer.observation.Observation;
|
||||||
@ -36,14 +37,19 @@ import org.springframework.context.ApplicationEventPublisher;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
|
||||||
import org.springframework.security.authentication.RememberMeAuthenticationToken;
|
import org.springframework.security.authentication.RememberMeAuthenticationToken;
|
||||||
import org.springframework.security.authentication.TestAuthentication;
|
import org.springframework.security.authentication.TestAuthentication;
|
||||||
|
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
import org.springframework.security.authorization.AuthorizationObservationContext;
|
import org.springframework.security.authorization.AuthorizationObservationContext;
|
||||||
|
import org.springframework.security.authorization.SingleResultAuthorizationManager;
|
||||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
|
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
|
||||||
import org.springframework.security.config.ObjectPostProcessor;
|
import org.springframework.security.config.ObjectPostProcessor;
|
||||||
@ -82,13 +88,17 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoInteractions;
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.springframework.security.config.Customizer.withDefaults;
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||||
@ -170,6 +180,26 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||||||
.withMessageContaining("manager cannot be null");
|
.withMessageContaining("manager cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenCustomAuthorizationManagerFactoryRegisteredThenUsed() {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class).autowire();
|
||||||
|
verify(authorizationManagerFactory).permitAll();
|
||||||
|
verify(authorizationManagerFactory).denyAll();
|
||||||
|
verify(authorizationManagerFactory).hasRole("ADMIN");
|
||||||
|
verify(authorizationManagerFactory).hasAnyRole("USER", "ADMIN");
|
||||||
|
verify(authorizationManagerFactory).hasAuthority("write");
|
||||||
|
verify(authorizationManagerFactory).hasAnyAuthority("resource.read", "read");
|
||||||
|
verify(authorizationManagerFactory).authenticated();
|
||||||
|
verify(authorizationManagerFactory).fullyAuthenticated();
|
||||||
|
verify(authorizationManagerFactory).rememberMe();
|
||||||
|
verify(authorizationManagerFactory).anonymous();
|
||||||
|
verifyNoMoreInteractions(authorizationManagerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {
|
public void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {
|
||||||
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
||||||
@ -538,6 +568,205 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||||||
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
|
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationManagerFactoryRegisteredAndPermitAllThenRespondsWithOk() throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> permitAll = spy(SingleResultAuthorizationManager.permitAll());
|
||||||
|
given(authorizationManagerFactory.permitAll()).willReturn(permitAll);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = get("/public").with(anonymous());
|
||||||
|
this.mvc.perform(request).andExpect(status().isOk());
|
||||||
|
verify(permitAll).authorize(any(), any(RequestAuthorizationContext.class));
|
||||||
|
verifyNoMoreInteractions(permitAll);
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationManagerFactoryRegisteredAndDenyAllThenRespondsWithForbidden()
|
||||||
|
throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> denyAll = spy(SingleResultAuthorizationManager.denyAll());
|
||||||
|
given(authorizationManagerFactory.denyAll()).willReturn(denyAll);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = get("/private").with(user("user"));
|
||||||
|
this.mvc.perform(request).andExpect(status().isForbidden());
|
||||||
|
verify(denyAll).authorize(any(), any(RequestAuthorizationContext.class));
|
||||||
|
verifyNoMoreInteractions(denyAll);
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationManagerFactoryRegisteredAndHasRoleThenRespondsWithOk() throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> hasRole = spy(AuthorityAuthorizationManager.hasRole("ADMIN"));
|
||||||
|
given(authorizationManagerFactory.hasRole(anyString())).willReturn(hasRole);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = get("/admin").with(user("admin").roles("ADMIN"));
|
||||||
|
this.mvc.perform(request).andExpect(status().isOk());
|
||||||
|
verify(hasRole).authorize(any(), any(RequestAuthorizationContext.class));
|
||||||
|
verifyNoMoreInteractions(hasRole);
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationManagerFactoryRegisteredAndHasAnyRoleThenRespondsWithOk() throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> hasAnyRole = spy(
|
||||||
|
AuthorityAuthorizationManager.hasAnyRole("USER", "ADMIN"));
|
||||||
|
given(authorizationManagerFactory.hasAnyRole(any(String[].class))).willReturn(hasAnyRole);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = get("/user").with(user("user").roles("USER"));
|
||||||
|
this.mvc.perform(request).andExpect(status().isOk());
|
||||||
|
verify(hasAnyRole).authorize(any(), any(RequestAuthorizationContext.class));
|
||||||
|
verifyNoMoreInteractions(hasAnyRole);
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void postWhenCustomAuthorizationManagerFactoryRegisteredAndHasAuthorityThenRespondsWithOk()
|
||||||
|
throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> hasAuthority = spy(
|
||||||
|
AuthorityAuthorizationManager.hasAuthority("write"));
|
||||||
|
given(authorizationManagerFactory.hasAuthority(anyString())).willReturn(hasAuthority);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = post("/resource")
|
||||||
|
.with(user("user").authorities(new SimpleGrantedAuthority("write")))
|
||||||
|
.with(csrf());
|
||||||
|
this.mvc.perform(request).andExpect(status().isOk());
|
||||||
|
verify(hasAuthority).authorize(any(), any(RequestAuthorizationContext.class));
|
||||||
|
verifyNoMoreInteractions(hasAuthority);
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationManagerFactoryRegisteredAndHasAnyAuthorityThenRespondsWithOk()
|
||||||
|
throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> hasAnyAuthority = spy(
|
||||||
|
AuthorityAuthorizationManager.hasAnyAuthority("resource.read", "read"));
|
||||||
|
given(authorizationManagerFactory.hasAnyAuthority(any(String[].class))).willReturn(hasAnyAuthority);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = get("/resource")
|
||||||
|
.with(user("user").authorities(new SimpleGrantedAuthority("read")));
|
||||||
|
this.mvc.perform(request).andExpect(status().isOk());
|
||||||
|
verify(hasAnyAuthority).authorize(any(), any(RequestAuthorizationContext.class));
|
||||||
|
verifyNoMoreInteractions(hasAnyAuthority);
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationManagerFactoryRegisteredAndAuthenticatedThenRespondsWithOk()
|
||||||
|
throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authenticated = spy(
|
||||||
|
AuthenticatedAuthorizationManager.authenticated());
|
||||||
|
given(authorizationManagerFactory.authenticated()).willReturn(authenticated);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = get("/authenticated").with(user("user"));
|
||||||
|
this.mvc.perform(request).andExpect(status().isOk());
|
||||||
|
verify(authenticated).authorize(any(), any(RequestAuthorizationContext.class));
|
||||||
|
verifyNoMoreInteractions(authenticated);
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationManagerFactoryRegisteredAndFullyAuthenticatedThenRespondsWithOk()
|
||||||
|
throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> fullyAuthenticated = spy(
|
||||||
|
AuthenticatedAuthorizationManager.fullyAuthenticated());
|
||||||
|
given(authorizationManagerFactory.fullyAuthenticated()).willReturn(fullyAuthenticated);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = get("/fully-authenticated").with(user("user"));
|
||||||
|
this.mvc.perform(request).andExpect(status().isOk());
|
||||||
|
verify(fullyAuthenticated).authorize(any(), any(RequestAuthorizationContext.class));
|
||||||
|
verifyNoMoreInteractions(fullyAuthenticated);
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationManagerFactoryRegisteredAndRememberMeThenRespondsWithOk() throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> rememberMe = spy(
|
||||||
|
AuthenticatedAuthorizationManager.rememberMe());
|
||||||
|
given(authorizationManagerFactory.rememberMe()).willReturn(rememberMe);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = get("/remember-me")
|
||||||
|
.with(authentication(new RememberMeAuthenticationToken("test", "user", Set.of())));
|
||||||
|
this.mvc.perform(request).andExpect(status().isOk());
|
||||||
|
verify(rememberMe).authorize(any(), any(RequestAuthorizationContext.class));
|
||||||
|
verifyNoMoreInteractions(rememberMe);
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationManagerFactoryRegisteredAndAnonymousThenRespondsWithOk() throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> anonymous = spy(
|
||||||
|
AuthenticatedAuthorizationManager.anonymous());
|
||||||
|
given(authorizationManagerFactory.anonymous()).willReturn(anonymous);
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = get("/anonymous").with(anonymous());
|
||||||
|
this.mvc.perform(request).andExpect(status().isOk());
|
||||||
|
verify(anonymous).authorize(any(), any(RequestAuthorizationContext.class));
|
||||||
|
verifyNoMoreInteractions(anonymous);
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationManagerFactoryRegisteredAndAccessThenRespondsWithForbidden()
|
||||||
|
throws Exception {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();
|
||||||
|
AuthorizationManagerFactoryConfig.authorizationManagerFactory = mockAuthorizationManagerFactory(
|
||||||
|
authorizationManager);
|
||||||
|
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfig.class).autowire();
|
||||||
|
MockHttpServletRequestBuilder request = get("/").with(user("user"));
|
||||||
|
this.mvc.perform(request).andExpect(status().isForbidden());
|
||||||
|
verifyNoInteractions(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getWhenExpressionHasIpAddressLocalhostConfiguredIpAddressIsLocalhostThenRespondsWithOk()
|
public void getWhenExpressionHasIpAddressLocalhostConfiguredIpAddressIsLocalhostThenRespondsWithOk()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@ -587,6 +816,23 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AuthorizationManagerFactory<RequestAuthorizationContext> mockAuthorizationManagerFactory(
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager) {
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mock();
|
||||||
|
given(authorizationManagerFactory.permitAll()).willReturn(authorizationManager);
|
||||||
|
given(authorizationManagerFactory.denyAll()).willReturn(authorizationManager);
|
||||||
|
given(authorizationManagerFactory.hasRole(anyString())).willReturn(authorizationManager);
|
||||||
|
given(authorizationManagerFactory.hasAnyRole(any(String[].class))).willReturn(authorizationManager);
|
||||||
|
given(authorizationManagerFactory.hasAuthority(anyString())).willReturn(authorizationManager);
|
||||||
|
given(authorizationManagerFactory.hasAnyAuthority(any(String[].class))).willReturn(authorizationManager);
|
||||||
|
given(authorizationManagerFactory.authenticated()).willReturn(authorizationManager);
|
||||||
|
given(authorizationManagerFactory.fullyAuthenticated()).willReturn(authorizationManager);
|
||||||
|
given(authorizationManagerFactory.rememberMe()).willReturn(authorizationManager);
|
||||||
|
given(authorizationManagerFactory.anonymous()).willReturn(authorizationManager);
|
||||||
|
|
||||||
|
return authorizationManagerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getWhenFullyAuthenticatedConfiguredAndRememberMeTokenThenRespondsWithUnauthorized() throws Exception {
|
public void getWhenFullyAuthenticatedConfiguredAndRememberMeTokenThenRespondsWithUnauthorized() throws Exception {
|
||||||
this.spring.register(FullyAuthenticatedConfig.class, BasicController.class).autowire();
|
this.spring.register(FullyAuthenticatedConfig.class, BasicController.class).autowire();
|
||||||
@ -850,6 +1096,41 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class AuthorizationManagerFactoryConfig {
|
||||||
|
|
||||||
|
static AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory() {
|
||||||
|
return authorizationManagerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize
|
||||||
|
.requestMatchers("/public").permitAll()
|
||||||
|
.requestMatchers("/private").denyAll()
|
||||||
|
.requestMatchers("/admin").hasRole("ADMIN")
|
||||||
|
.requestMatchers("/user").hasAnyRole("USER", "ADMIN")
|
||||||
|
.requestMatchers(HttpMethod.POST, "/resource").hasAuthority("write")
|
||||||
|
.requestMatchers("/resource").hasAnyAuthority("resource.read", "read")
|
||||||
|
.requestMatchers("/authenticated").authenticated()
|
||||||
|
.requestMatchers("/fully-authenticated").fullyAuthenticated()
|
||||||
|
.requestMatchers("/remember-me").rememberMe()
|
||||||
|
.requestMatchers("/anonymous").anonymous()
|
||||||
|
.anyRequest().access((authentication, context) -> new AuthorizationDecision(false))
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class ObjectPostProcessorConfig {
|
static class ObjectPostProcessorConfig {
|
||||||
@ -1295,6 +1576,47 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class AccessTestController {
|
||||||
|
|
||||||
|
@RequestMapping("/public")
|
||||||
|
void publicEndpoint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/private")
|
||||||
|
void privateEndpoint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/admin")
|
||||||
|
void adminEndpoint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/user")
|
||||||
|
void userEndpoint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/resource")
|
||||||
|
void resourceEndpoint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/authenticated")
|
||||||
|
void authenticatedEndpoint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/fully-authenticated")
|
||||||
|
void fullyAuthenticatedEndpoint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/remember-me")
|
||||||
|
void rememberMeEndpoint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/anonymous")
|
||||||
|
void anonymousEndpoint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class ObservationRegistryConfig {
|
static class ObservationRegistryConfig {
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
|
|||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
import org.springframework.security.access.PermissionEvaluator;
|
import org.springframework.security.access.PermissionEvaluator;
|
||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
|
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@ -38,6 +40,7 @@ import org.springframework.util.Assert;
|
|||||||
*
|
*
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @author Evgeniy Cheban
|
* @author Evgeniy Cheban
|
||||||
|
* @author Steve Riesenberg
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractSecurityExpressionHandler<T>
|
public abstract class AbstractSecurityExpressionHandler<T>
|
||||||
@ -49,6 +52,8 @@ public abstract class AbstractSecurityExpressionHandler<T>
|
|||||||
|
|
||||||
private @Nullable RoleHierarchy roleHierarchy;
|
private @Nullable RoleHierarchy roleHierarchy;
|
||||||
|
|
||||||
|
private AuthorizationManagerFactory<T> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
|
||||||
private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
|
private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -106,11 +111,58 @@ public abstract class AbstractSecurityExpressionHandler<T>
|
|||||||
protected abstract SecurityExpressionOperations createSecurityExpressionRoot(
|
protected abstract SecurityExpressionOperations createSecurityExpressionRoot(
|
||||||
@Nullable Authentication authentication, T invocation);
|
@Nullable Authentication authentication, T invocation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link AuthorizationManagerFactory} to be used. The default is
|
||||||
|
* {@link DefaultAuthorizationManagerFactory}.
|
||||||
|
* @param authorizationManagerFactory the {@link AuthorizationManagerFactory} to use.
|
||||||
|
* Cannot be null.
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public final void setAuthorizationManagerFactory(AuthorizationManagerFactory<T> authorizationManagerFactory) {
|
||||||
|
Assert.notNull(authorizationManagerFactory, "authorizationManagerFactory cannot be null");
|
||||||
|
this.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final AuthorizationManagerFactory<T> getAuthorizationManagerFactory() {
|
||||||
|
return this.authorizationManagerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows accessing the {@link DefaultAuthorizationManagerFactory} for getting and
|
||||||
|
* setting defaults. This method will be removed in Spring Security 8.
|
||||||
|
* @return the {@link DefaultAuthorizationManagerFactory}
|
||||||
|
* @throws IllegalStateException if a different {@link AuthorizationManagerFactory}
|
||||||
|
* was already set
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
|
protected final DefaultAuthorizationManagerFactory<T> getDefaultAuthorizationManagerFactory() {
|
||||||
|
if (!(this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<T> defaultAuthorizationManagerFactory)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"authorizationManagerFactory must be an instance of DefaultAuthorizationManagerFactory");
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultAuthorizationManagerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #getDefaultAuthorizationManagerFactory()} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
protected @Nullable RoleHierarchy getRoleHierarchy() {
|
protected @Nullable RoleHierarchy getRoleHierarchy() {
|
||||||
return this.roleHierarchy;
|
return this.roleHierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
|
/**
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
|
public void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {
|
||||||
|
if (roleHierarchy != null) {
|
||||||
|
getDefaultAuthorizationManagerFactory().setRoleHierarchy(roleHierarchy);
|
||||||
|
}
|
||||||
this.roleHierarchy = roleHierarchy;
|
this.roleHierarchy = roleHierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
package org.springframework.security.access.expression;
|
package org.springframework.security.access.expression;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
@ -26,10 +24,11 @@ import org.jspecify.annotations.Nullable;
|
|||||||
import org.springframework.security.access.PermissionEvaluator;
|
import org.springframework.security.access.PermissionEvaluator;
|
||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
|
import org.springframework.security.authorization.AuthorizationResult;
|
||||||
|
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.function.SingletonSupplier;
|
import org.springframework.util.function.SingletonSupplier;
|
||||||
|
|
||||||
@ -38,20 +37,19 @@ import org.springframework.util.function.SingletonSupplier;
|
|||||||
*
|
*
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @author Evgeniy Cheban
|
* @author Evgeniy Cheban
|
||||||
|
* @author Steve Riesenberg
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public abstract class SecurityExpressionRoot implements SecurityExpressionOperations {
|
public abstract class SecurityExpressionRoot<T extends @Nullable Object> implements SecurityExpressionOperations {
|
||||||
|
|
||||||
private final Supplier<Authentication> authentication;
|
private final Supplier<Authentication> authentication;
|
||||||
|
|
||||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
|
||||||
|
|
||||||
private @Nullable RoleHierarchy roleHierarchy;
|
|
||||||
|
|
||||||
private @Nullable Set<String> roles;
|
|
||||||
|
|
||||||
private String defaultRolePrefix = "ROLE_";
|
private String defaultRolePrefix = "ROLE_";
|
||||||
|
|
||||||
|
private final T object;
|
||||||
|
|
||||||
|
private AuthorizationManagerFactory<T> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows "permitAll" expression
|
* Allows "permitAll" expression
|
||||||
*/
|
*/
|
||||||
@ -77,9 +75,12 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||||||
/**
|
/**
|
||||||
* Creates a new instance
|
* Creates a new instance
|
||||||
* @param authentication the {@link Authentication} to use. Cannot be null.
|
* @param authentication the {@link Authentication} to use. Cannot be null.
|
||||||
|
* @deprecated Use {@link #SecurityExpressionRoot(Supplier, Object)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
public SecurityExpressionRoot(@Nullable Authentication authentication) {
|
public SecurityExpressionRoot(@Nullable Authentication authentication) {
|
||||||
this(() -> authentication);
|
this(() -> authentication, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,44 +89,70 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to use.
|
* @param authentication the {@link Supplier} of the {@link Authentication} to use.
|
||||||
* Cannot be null.
|
* Cannot be null.
|
||||||
* @since 5.8
|
* @since 5.8
|
||||||
|
* @deprecated Use {@link #SecurityExpressionRoot(Supplier, Object)} instead
|
||||||
*/
|
*/
|
||||||
public SecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication) {
|
@Deprecated(since = "7.0")
|
||||||
|
@SuppressWarnings("NullAway")
|
||||||
|
public SecurityExpressionRoot(Supplier<@Nullable Authentication> authentication) {
|
||||||
|
this(authentication, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance that uses lazy initialization of the {@link Authentication}
|
||||||
|
* object.
|
||||||
|
* @param authentication the {@link Supplier} of the {@link Authentication} to use.
|
||||||
|
* Cannot be null.
|
||||||
|
* @param object the object being authorized
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public SecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication, T object) {
|
||||||
this.authentication = SingletonSupplier.of(() -> {
|
this.authentication = SingletonSupplier.of(() -> {
|
||||||
Authentication value = authentication.get();
|
Authentication value = authentication.get();
|
||||||
Assert.notNull(value, "Authentication object cannot be null");
|
Assert.notNull(value, "Authentication object cannot be null");
|
||||||
return value;
|
return value;
|
||||||
});
|
});
|
||||||
|
this.object = object;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean hasAuthority(String authority) {
|
public final boolean hasAuthority(String authority) {
|
||||||
return hasAnyAuthority(authority);
|
return isGranted(this.authorizationManagerFactory.hasAnyAuthority(authority));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean hasAnyAuthority(String... authorities) {
|
public final boolean hasAnyAuthority(String... authorities) {
|
||||||
return hasAnyAuthorityName(null, authorities);
|
return isGranted(this.authorizationManagerFactory.hasAnyAuthority(authorities));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean hasRole(String role) {
|
public final boolean hasRole(String role) {
|
||||||
return hasAnyRole(role);
|
if (this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<T>) {
|
||||||
|
// To provide passivity for old behavior where hasRole('ROLE_A') is allowed,
|
||||||
|
// we strip the role prefix when found.
|
||||||
|
// TODO: Remove in favor of fixing inconsistent behavior?
|
||||||
|
String rolePrefix = this.defaultRolePrefix;
|
||||||
|
if (role.startsWith(rolePrefix)) {
|
||||||
|
role = role.substring(rolePrefix.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isGranted(this.authorizationManagerFactory.hasRole(role));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean hasAnyRole(String... roles) {
|
public final boolean hasAnyRole(String... roles) {
|
||||||
return hasAnyAuthorityName(this.defaultRolePrefix, roles);
|
if (this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<T>) {
|
||||||
}
|
// To provide passivity for old behavior where hasRole('ROLE_A') is allowed,
|
||||||
|
// we strip the role prefix when found.
|
||||||
private boolean hasAnyAuthorityName(@Nullable String prefix, String... roles) {
|
// TODO: Remove in favor of fixing inconsistent behavior?
|
||||||
Set<String> roleSet = getAuthoritySet();
|
String rolePrefix = this.defaultRolePrefix;
|
||||||
for (String role : roles) {
|
for (int index = 0; index < roles.length; index++) {
|
||||||
String defaultedRole = getRoleWithDefaultPrefix(prefix, role);
|
String role = roles[index];
|
||||||
if (roleSet.contains(defaultedRole)) {
|
if (role.startsWith(rolePrefix)) {
|
||||||
return true;
|
roles[index] = role.substring(rolePrefix.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
return isGranted(this.authorizationManagerFactory.hasAnyRole(roles));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -135,33 +162,37 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean permitAll() {
|
public final boolean permitAll() {
|
||||||
return true;
|
return isGranted(this.authorizationManagerFactory.permitAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean denyAll() {
|
public final boolean denyAll() {
|
||||||
return false;
|
return isGranted(this.authorizationManagerFactory.denyAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isAnonymous() {
|
public final boolean isAnonymous() {
|
||||||
return this.trustResolver.isAnonymous(getAuthentication());
|
return isGranted(this.authorizationManagerFactory.anonymous());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isAuthenticated() {
|
public final boolean isAuthenticated() {
|
||||||
return this.trustResolver.isAuthenticated(getAuthentication());
|
return isGranted(this.authorizationManagerFactory.authenticated());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isRememberMe() {
|
public final boolean isRememberMe() {
|
||||||
return this.trustResolver.isRememberMe(getAuthentication());
|
return isGranted(this.authorizationManagerFactory.rememberMe());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isFullyAuthenticated() {
|
public final boolean isFullyAuthenticated() {
|
||||||
Authentication authentication = getAuthentication();
|
return isGranted(this.authorizationManagerFactory.fullyAuthenticated());
|
||||||
return this.trustResolver.isFullyAuthenticated(authentication);
|
}
|
||||||
|
|
||||||
|
private boolean isGranted(AuthorizationManager<T> authorizationManager) {
|
||||||
|
AuthorizationResult authorizationResult = authorizationManager.authorize(this.authentication, this.object);
|
||||||
|
return (authorizationResult != null && authorizationResult.isGranted());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,12 +204,24 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||||||
return getAuthentication().getPrincipal();
|
return getAuthentication().getPrincipal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
||||||
this.trustResolver = trustResolver;
|
getDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {
|
public void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {
|
||||||
this.roleHierarchy = roleHierarchy;
|
if (roleHierarchy != null) {
|
||||||
|
getDefaultAuthorizationManagerFactory().setRoleHierarchy(roleHierarchy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -193,20 +236,46 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||||||
* If null or empty, then no default role prefix is used.
|
* If null or empty, then no default role prefix is used.
|
||||||
* </p>
|
* </p>
|
||||||
* @param defaultRolePrefix the default prefix to add to roles. Default "ROLE_".
|
* @param defaultRolePrefix the default prefix to add to roles. Default "ROLE_".
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
public void setDefaultRolePrefix(String defaultRolePrefix) {
|
@Deprecated(since = "7.0")
|
||||||
|
public void setDefaultRolePrefix(@Nullable String defaultRolePrefix) {
|
||||||
|
if (defaultRolePrefix == null) {
|
||||||
|
defaultRolePrefix = "";
|
||||||
|
}
|
||||||
|
getDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);
|
||||||
this.defaultRolePrefix = defaultRolePrefix;
|
this.defaultRolePrefix = defaultRolePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getAuthoritySet() {
|
/**
|
||||||
if (this.roles == null) {
|
* Sets the {@link AuthorizationManagerFactory} to use for creating instances of
|
||||||
Collection<? extends GrantedAuthority> userAuthorities = getAuthentication().getAuthorities();
|
* {@link AuthorizationManager}.
|
||||||
if (this.roleHierarchy != null) {
|
* @param authorizationManagerFactory the {@link AuthorizationManagerFactory} to use
|
||||||
userAuthorities = this.roleHierarchy.getReachableGrantedAuthorities(userAuthorities);
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public void setAuthorizationManagerFactory(AuthorizationManagerFactory<T> authorizationManagerFactory) {
|
||||||
|
Assert.notNull(authorizationManagerFactory, "authorizationManagerFactory cannot be null");
|
||||||
|
this.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
}
|
}
|
||||||
this.roles = AuthorityUtils.authorityListToSet(userAuthorities);
|
|
||||||
|
/**
|
||||||
|
* Allows accessing the {@link DefaultAuthorizationManagerFactory} for getting and
|
||||||
|
* setting defaults. This method will be removed in Spring Security 8.
|
||||||
|
* @return the {@link DefaultAuthorizationManagerFactory}
|
||||||
|
* @throws IllegalStateException if a different {@link AuthorizationManagerFactory}
|
||||||
|
* was already set
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "7.0", forRemoval = true)
|
||||||
|
private DefaultAuthorizationManagerFactory<T> getDefaultAuthorizationManagerFactory() {
|
||||||
|
if (!(this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<T> defaultAuthorizationManagerFactory)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"authorizationManagerFactory must be an instance of DefaultAuthorizationManagerFactory");
|
||||||
}
|
}
|
||||||
return this.roles;
|
|
||||||
|
return defaultAuthorizationManagerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -225,24 +294,4 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||||||
this.permissionEvaluator = permissionEvaluator;
|
this.permissionEvaluator = permissionEvaluator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Prefixes role with defaultRolePrefix if defaultRolePrefix is non-null and if role
|
|
||||||
* does not already start with defaultRolePrefix.
|
|
||||||
* @param defaultRolePrefix
|
|
||||||
* @param role
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static String getRoleWithDefaultPrefix(@Nullable String defaultRolePrefix, String role) {
|
|
||||||
if (role == null) {
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
if (defaultRolePrefix == null || defaultRolePrefix.length() == 0) {
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
if (role.startsWith(defaultRolePrefix)) {
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
return defaultRolePrefix + role;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ import org.springframework.security.access.expression.AbstractSecurityExpression
|
|||||||
import org.springframework.security.access.expression.ExpressionUtils;
|
import org.springframework.security.access.expression.ExpressionUtils;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.parameters.DefaultSecurityParameterNameDiscoverer;
|
import org.springframework.security.core.parameters.DefaultSecurityParameterNameDiscoverer;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -56,11 +57,14 @@ import org.springframework.util.Assert;
|
|||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @author Evgeniy Cheban
|
* @author Evgeniy Cheban
|
||||||
* @author Blagoja Stamatovski
|
* @author Blagoja Stamatovski
|
||||||
|
* @author Steve Riesenberg
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler<MethodInvocation>
|
public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler<MethodInvocation>
|
||||||
implements MethodSecurityExpressionHandler {
|
implements MethodSecurityExpressionHandler {
|
||||||
|
|
||||||
|
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
|
||||||
|
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||||
@ -69,7 +73,7 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
|
|||||||
|
|
||||||
private @Nullable PermissionCacheOptimizer permissionCacheOptimizer = null;
|
private @Nullable PermissionCacheOptimizer permissionCacheOptimizer = null;
|
||||||
|
|
||||||
private String defaultRolePrefix = "ROLE_";
|
private String defaultRolePrefix = DEFAULT_ROLE_PREFIX;
|
||||||
|
|
||||||
public DefaultMethodSecurityExpressionHandler() {
|
public DefaultMethodSecurityExpressionHandler() {
|
||||||
}
|
}
|
||||||
@ -106,12 +110,14 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
|
|||||||
|
|
||||||
private MethodSecurityExpressionOperations createSecurityExpressionRoot(
|
private MethodSecurityExpressionOperations createSecurityExpressionRoot(
|
||||||
Supplier<? extends @Nullable Authentication> authentication, MethodInvocation invocation) {
|
Supplier<? extends @Nullable Authentication> authentication, MethodInvocation invocation) {
|
||||||
MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
|
MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication, invocation);
|
||||||
root.setThis(invocation.getThis());
|
root.setThis(invocation.getThis());
|
||||||
|
root.setAuthorizationManagerFactory(getAuthorizationManagerFactory());
|
||||||
root.setPermissionEvaluator(getPermissionEvaluator());
|
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||||
root.setTrustResolver(getTrustResolver());
|
if (!DEFAULT_ROLE_PREFIX.equals(this.defaultRolePrefix)) {
|
||||||
Optional.ofNullable(getRoleHierarchy()).ifPresent(root::setRoleHierarchy);
|
// Ensure SecurityExpressionRoot can strip the custom role prefix
|
||||||
root.setDefaultRolePrefix(getDefaultRolePrefix());
|
root.setDefaultRolePrefix(getDefaultRolePrefix());
|
||||||
|
}
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,15 +238,22 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
|
|||||||
* {@link AuthenticationTrustResolverImpl}.
|
* {@link AuthenticationTrustResolverImpl}.
|
||||||
* @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be
|
* @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be
|
||||||
* null.
|
* null.
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
||||||
Assert.notNull(trustResolver, "trustResolver cannot be null");
|
Assert.notNull(trustResolver, "trustResolver cannot be null");
|
||||||
|
getDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);
|
||||||
this.trustResolver = trustResolver;
|
this.trustResolver = trustResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The current {@link AuthenticationTrustResolver}
|
* @return The current {@link AuthenticationTrustResolver}
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
protected AuthenticationTrustResolver getTrustResolver() {
|
protected AuthenticationTrustResolver getTrustResolver() {
|
||||||
return this.trustResolver;
|
return this.trustResolver;
|
||||||
}
|
}
|
||||||
@ -289,14 +302,24 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
|
|||||||
* If null or empty, then no default role prefix is used.
|
* If null or empty, then no default role prefix is used.
|
||||||
* </p>
|
* </p>
|
||||||
* @param defaultRolePrefix the default prefix to add to roles. Default "ROLE_".
|
* @param defaultRolePrefix the default prefix to add to roles. Default "ROLE_".
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
public void setDefaultRolePrefix(String defaultRolePrefix) {
|
@Deprecated(since = "7.0")
|
||||||
|
public void setDefaultRolePrefix(@Nullable String defaultRolePrefix) {
|
||||||
|
if (defaultRolePrefix == null) {
|
||||||
|
defaultRolePrefix = "";
|
||||||
|
}
|
||||||
|
getDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);
|
||||||
this.defaultRolePrefix = defaultRolePrefix;
|
this.defaultRolePrefix = defaultRolePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The default role prefix
|
* @return The default role prefix
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
protected String getDefaultRolePrefix() {
|
protected String getDefaultRolePrefix() {
|
||||||
return this.defaultRolePrefix;
|
return this.defaultRolePrefix;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package org.springframework.security.access.expression.method;
|
|||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||||
@ -28,9 +29,11 @@ import org.springframework.security.core.Authentication;
|
|||||||
*
|
*
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @author Evgeniy Cheban
|
* @author Evgeniy Cheban
|
||||||
|
* @author Steve Riesenberg
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
|
class MethodSecurityExpressionRoot extends SecurityExpressionRoot<MethodInvocation>
|
||||||
|
implements MethodSecurityExpressionOperations {
|
||||||
|
|
||||||
private @Nullable Object filterObject;
|
private @Nullable Object filterObject;
|
||||||
|
|
||||||
@ -38,12 +41,9 @@ class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements Met
|
|||||||
|
|
||||||
private @Nullable Object target;
|
private @Nullable Object target;
|
||||||
|
|
||||||
MethodSecurityExpressionRoot(@Nullable Authentication a) {
|
MethodSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
super(a);
|
MethodInvocation methodInvocation) {
|
||||||
}
|
super(authentication, methodInvocation);
|
||||||
|
|
||||||
MethodSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication) {
|
|
||||||
super(authentication);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.authorization;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory for creating different kinds of {@link AuthorizationManager} instances.
|
||||||
|
*
|
||||||
|
* @param <T> the type of object that the authorization check is being done on
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public interface AuthorizationManagerFactory<T extends @Nullable Object> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@link AuthorizationManager} that allows anyone.
|
||||||
|
* @return A new {@link AuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
default AuthorizationManager<T> permitAll() {
|
||||||
|
return SingleResultAuthorizationManager.permitAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link AuthorizationManager} that does not allow anyone.
|
||||||
|
* @return A new {@link AuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
default AuthorizationManager<T> denyAll() {
|
||||||
|
return SingleResultAuthorizationManager.denyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link AuthorizationManager} that requires users to have the specified
|
||||||
|
* role.
|
||||||
|
* @param role the role (automatically prepended with ROLE_) that should be required
|
||||||
|
* to allow access (i.e. USER, ADMIN, etc.)
|
||||||
|
* @return A new {@link AuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
default AuthorizationManager<T> hasRole(String role) {
|
||||||
|
return AuthorityAuthorizationManager.hasRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link AuthorizationManager} that requires users to have one of many
|
||||||
|
* roles.
|
||||||
|
* @param roles the roles (automatically prepended with ROLE_) that the user should
|
||||||
|
* have at least one of to allow access (i.e. USER, ADMIN, etc.)
|
||||||
|
* @return A new {@link AuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
default AuthorizationManager<T> hasAnyRole(String... roles) {
|
||||||
|
return AuthorityAuthorizationManager.hasAnyRole(roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link AuthorizationManager} that requires users to have the specified
|
||||||
|
* authority.
|
||||||
|
* @param authority the authority that should be required to allow access (i.e.
|
||||||
|
* ROLE_USER, ROLE_ADMIN, etc.)
|
||||||
|
* @return A new {@link AuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
default AuthorizationManager<T> hasAuthority(String authority) {
|
||||||
|
return AuthorityAuthorizationManager.hasAuthority(authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link AuthorizationManager} that requires users to have one of many
|
||||||
|
* authorities.
|
||||||
|
* @param authorities the authorities that the user should have at least one of to
|
||||||
|
* allow access (i.e. ROLE_USER, ROLE_ADMIN, etc.)
|
||||||
|
* @return A new {@link AuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
default AuthorizationManager<T> hasAnyAuthority(String... authorities) {
|
||||||
|
return AuthorityAuthorizationManager.hasAnyAuthority(authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link AuthorizationManager} that allows any authenticated user.
|
||||||
|
* @return A new {@link AuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
default AuthorizationManager<T> authenticated() {
|
||||||
|
return AuthenticatedAuthorizationManager.authenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link AuthorizationManager} that allows users who have authenticated
|
||||||
|
* and were not remembered.
|
||||||
|
* @return A new {@link AuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
default AuthorizationManager<T> fullyAuthenticated() {
|
||||||
|
return AuthenticatedAuthorizationManager.fullyAuthenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link AuthorizationManager} that allows users that have been
|
||||||
|
* remembered.
|
||||||
|
* @return A new {@link AuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
default AuthorizationManager<T> rememberMe() {
|
||||||
|
return AuthenticatedAuthorizationManager.rememberMe();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link AuthorizationManager} that allows only anonymous users.
|
||||||
|
* @return A new {@link AuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
default AuthorizationManager<T> anonymous() {
|
||||||
|
return AuthenticatedAuthorizationManager.anonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.authorization;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
|
||||||
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory for creating different kinds of {@link AuthorizationManager} instances.
|
||||||
|
*
|
||||||
|
* @param <T> the type of object that the authorization check is being done on
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object>
|
||||||
|
implements AuthorizationManagerFactory<T> {
|
||||||
|
|
||||||
|
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||||
|
|
||||||
|
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
|
||||||
|
|
||||||
|
private String rolePrefix = "ROLE_";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link AuthenticationTrustResolver} used to check the user's
|
||||||
|
* authentication.
|
||||||
|
* @param trustResolver the {@link AuthenticationTrustResolver} to use
|
||||||
|
*/
|
||||||
|
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
||||||
|
Assert.notNull(trustResolver, "trustResolver cannot be null");
|
||||||
|
this.trustResolver = trustResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link RoleHierarchy} used to discover reachable authorities.
|
||||||
|
* @param roleHierarchy the {@link RoleHierarchy} to use
|
||||||
|
*/
|
||||||
|
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
|
||||||
|
Assert.notNull(roleHierarchy, "roleHierarchy cannot be null");
|
||||||
|
this.roleHierarchy = roleHierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the prefix used to create an authority name from a role name. Can be an empty
|
||||||
|
* string.
|
||||||
|
* @param rolePrefix the role prefix to use
|
||||||
|
*/
|
||||||
|
public void setRolePrefix(String rolePrefix) {
|
||||||
|
Assert.notNull(rolePrefix, "rolePrefix cannot be null");
|
||||||
|
this.rolePrefix = rolePrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<T> hasRole(String role) {
|
||||||
|
return hasAnyRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<T> hasAnyRole(String... roles) {
|
||||||
|
return withRoleHierarchy(AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, roles));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<T> hasAuthority(String authority) {
|
||||||
|
return withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<T> hasAnyAuthority(String... authorities) {
|
||||||
|
return withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<T> authenticated() {
|
||||||
|
return withTrustResolver(AuthenticatedAuthorizationManager.authenticated());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<T> fullyAuthenticated() {
|
||||||
|
return withTrustResolver(AuthenticatedAuthorizationManager.fullyAuthenticated());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<T> rememberMe() {
|
||||||
|
return withTrustResolver(AuthenticatedAuthorizationManager.rememberMe());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<T> anonymous() {
|
||||||
|
return withTrustResolver(AuthenticatedAuthorizationManager.anonymous());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthorityAuthorizationManager<T> withRoleHierarchy(AuthorityAuthorizationManager<T> authorizationManager) {
|
||||||
|
authorizationManager.setRoleHierarchy(this.roleHierarchy);
|
||||||
|
return authorizationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthenticatedAuthorizationManager<T> withTrustResolver(
|
||||||
|
AuthenticatedAuthorizationManager<T> authorizationManager) {
|
||||||
|
authorizationManager.setTrustResolver(this.trustResolver);
|
||||||
|
return authorizationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.security.access.expression.method;
|
package org.springframework.security.access.expression.method;
|
||||||
|
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ public class MethodSecurityExpressionRootTests {
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void createContext() {
|
public void createContext() {
|
||||||
this.user = mock(Authentication.class);
|
this.user = mock(Authentication.class);
|
||||||
this.root = new MethodSecurityExpressionRoot(this.user);
|
this.root = new MethodSecurityExpressionRoot(() -> this.user, mock(MethodInvocation.class));
|
||||||
this.ctx = new StandardEvaluationContext();
|
this.ctx = new StandardEvaluationContext();
|
||||||
this.ctx.setRootObject(this.root);
|
this.ctx.setRootObject(this.root);
|
||||||
this.trustResolver = mock(AuthenticationTrustResolver.class);
|
this.trustResolver = mock(AuthenticationTrustResolver.class);
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.authorization;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link AuthorizationManagerFactory}.
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
public class AuthorizationManagerFactoryTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void permitAllReturnsSingleResultAuthorizationManagerByDefault() {
|
||||||
|
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
AuthorizationManager<String> authorizationManager = factory.permitAll();
|
||||||
|
assertThat(authorizationManager).isInstanceOf(SingleResultAuthorizationManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void denyAllReturnsSingleResultAuthorizationManagerByDefault() {
|
||||||
|
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
AuthorizationManager<String> authorizationManager = factory.denyAll();
|
||||||
|
assertThat(authorizationManager).isInstanceOf(SingleResultAuthorizationManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasRoleReturnsAuthorityAuthorizationManagerByDefault() {
|
||||||
|
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
AuthorizationManager<String> authorizationManager = factory.hasRole("USER");
|
||||||
|
assertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyRoleReturnsAuthorityAuthorizationManagerByDefault() {
|
||||||
|
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
AuthorizationManager<String> authorizationManager = factory.hasAnyRole("USER", "ADMIN");
|
||||||
|
assertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAuthorityReturnsAuthorityAuthorizationManagerByDefault() {
|
||||||
|
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
AuthorizationManager<String> authorizationManager = factory.hasAuthority("authority1");
|
||||||
|
assertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyAuthorityReturnsAuthorityAuthorizationManagerByDefault() {
|
||||||
|
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
AuthorizationManager<String> authorizationManager = factory.hasAnyAuthority("authority1", "authority2");
|
||||||
|
assertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticatedReturnsAuthenticatedAuthorizationManagerByDefault() {
|
||||||
|
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
AuthorizationManager<String> authorizationManager = factory.authenticated();
|
||||||
|
assertThat(authorizationManager).isInstanceOf(AuthenticatedAuthorizationManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fullyAuthenticatedReturnsAuthenticatedAuthorizationManagerByDefault() {
|
||||||
|
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
AuthorizationManager<String> authorizationManager = factory.fullyAuthenticated();
|
||||||
|
assertThat(authorizationManager).isInstanceOf(AuthenticatedAuthorizationManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rememberMeReturnsAuthenticatedAuthorizationManagerByDefault() {
|
||||||
|
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
AuthorizationManager<String> authorizationManager = factory.rememberMe();
|
||||||
|
assertThat(authorizationManager).isInstanceOf(AuthenticatedAuthorizationManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void anonymousReturnsAuthenticatedAuthorizationManagerByDefault() {
|
||||||
|
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
AuthorizationManager<String> authorizationManager = factory.anonymous();
|
||||||
|
assertThat(authorizationManager).isInstanceOf(AuthenticatedAuthorizationManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -26,6 +26,8 @@ import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
|
|||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
|
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
@ -92,18 +94,18 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public class SecurityEvaluationContextExtension implements EvaluationContextExtension {
|
public class SecurityEvaluationContextExtension implements EvaluationContextExtension {
|
||||||
|
|
||||||
|
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
|
||||||
|
|
||||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||||
.getContextHolderStrategy();
|
.getContextHolderStrategy();
|
||||||
|
|
||||||
private @Nullable Authentication authentication;
|
private @Nullable Authentication authentication;
|
||||||
|
|
||||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
private AuthorizationManagerFactory<Object> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();
|
||||||
|
|
||||||
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
|
|
||||||
|
|
||||||
private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
|
private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
|
||||||
|
|
||||||
private String defaultRolePrefix = "ROLE_";
|
private String defaultRolePrefix = DEFAULT_ROLE_PREFIX;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance that uses the current {@link Authentication} found on the
|
* Creates a new instance that uses the current {@link Authentication} found on the
|
||||||
@ -126,14 +128,16 @@ public class SecurityEvaluationContextExtension implements EvaluationContextExte
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecurityExpressionRoot getRootObject() {
|
public SecurityExpressionRoot<Object> getRootObject() {
|
||||||
Authentication authentication = getAuthentication();
|
Authentication authentication = getAuthentication();
|
||||||
SecurityExpressionRoot root = new SecurityExpressionRoot(authentication) {
|
SecurityExpressionRoot<Object> root = new SecurityExpressionRoot<>(() -> authentication, new Object()) {
|
||||||
};
|
};
|
||||||
root.setTrustResolver(this.trustResolver);
|
root.setAuthorizationManagerFactory(this.authorizationManagerFactory);
|
||||||
root.setRoleHierarchy(this.roleHierarchy);
|
|
||||||
root.setPermissionEvaluator(this.permissionEvaluator);
|
root.setPermissionEvaluator(this.permissionEvaluator);
|
||||||
|
if (!DEFAULT_ROLE_PREFIX.equals(this.defaultRolePrefix)) {
|
||||||
|
// Ensure SecurityExpressionRoot can strip the custom role prefix
|
||||||
root.setDefaultRolePrefix(this.defaultRolePrefix);
|
root.setDefaultRolePrefix(this.defaultRolePrefix);
|
||||||
|
}
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,15 +160,46 @@ public class SecurityEvaluationContextExtension implements EvaluationContextExte
|
|||||||
return context.getAuthentication();
|
return context.getAuthentication();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link AuthorizationManagerFactory} to be used. The default is
|
||||||
|
* {@link DefaultAuthorizationManagerFactory}.
|
||||||
|
* @param authorizationManagerFactory the {@link AuthorizationManagerFactory} to use.
|
||||||
|
* Cannot be null.
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public void setAuthorizationManagerFactory(AuthorizationManagerFactory<Object> authorizationManagerFactory) {
|
||||||
|
Assert.notNull(authorizationManagerFactory, "authorizationManagerFactory cannot be null");
|
||||||
|
this.authorizationManagerFactory = authorizationManagerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows accessing the {@link DefaultAuthorizationManagerFactory} for getting and
|
||||||
|
* setting defaults. This method will be removed in Spring Security 8.
|
||||||
|
* @return the {@link DefaultAuthorizationManagerFactory}
|
||||||
|
* @throws IllegalStateException if a different {@link AuthorizationManagerFactory}
|
||||||
|
* was already set
|
||||||
|
*/
|
||||||
|
private DefaultAuthorizationManagerFactory<Object> getDefaultAuthorizationManagerFactory() {
|
||||||
|
if (!(this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<Object> defaultAuthorizationManagerFactory)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"authorizationManagerFactory must be an instance of DefaultAuthorizationManagerFactory");
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultAuthorizationManagerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link AuthenticationTrustResolver} to be used. Default is
|
* Sets the {@link AuthenticationTrustResolver} to be used. Default is
|
||||||
* {@link AuthenticationTrustResolverImpl}. Cannot be null.
|
* {@link AuthenticationTrustResolverImpl}. Cannot be null.
|
||||||
* @param trustResolver the {@link AuthenticationTrustResolver} to use
|
* @param trustResolver the {@link AuthenticationTrustResolver} to use
|
||||||
* @since 5.8
|
* @since 5.8
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
||||||
Assert.notNull(trustResolver, "trustResolver cannot be null");
|
Assert.notNull(trustResolver, "trustResolver cannot be null");
|
||||||
this.trustResolver = trustResolver;
|
getDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,10 +207,13 @@ public class SecurityEvaluationContextExtension implements EvaluationContextExte
|
|||||||
* Cannot be null.
|
* Cannot be null.
|
||||||
* @param roleHierarchy the {@link RoleHierarchy} to use
|
* @param roleHierarchy the {@link RoleHierarchy} to use
|
||||||
* @since 5.8
|
* @since 5.8
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
|
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
|
||||||
Assert.notNull(roleHierarchy, "roleHierarchy cannot be null");
|
Assert.notNull(roleHierarchy, "roleHierarchy cannot be null");
|
||||||
this.roleHierarchy = roleHierarchy;
|
getDefaultAuthorizationManagerFactory().setRoleHierarchy(roleHierarchy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -199,8 +237,12 @@ public class SecurityEvaluationContextExtension implements EvaluationContextExte
|
|||||||
* @param defaultRolePrefix the default prefix to add to roles. The default is
|
* @param defaultRolePrefix the default prefix to add to roles. The default is
|
||||||
* "ROLE_".
|
* "ROLE_".
|
||||||
* @since 5.8
|
* @since 5.8
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public void setDefaultRolePrefix(String defaultRolePrefix) {
|
public void setDefaultRolePrefix(String defaultRolePrefix) {
|
||||||
|
getDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);
|
||||||
this.defaultRolePrefix = defaultRolePrefix;
|
this.defaultRolePrefix = defaultRolePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,10 +23,9 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.springframework.security.access.PermissionEvaluator;
|
import org.springframework.security.access.PermissionEvaluator;
|
||||||
import org.springframework.security.access.expression.DenyAllPermissionEvaluator;
|
import org.springframework.security.access.expression.DenyAllPermissionEvaluator;
|
||||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||||
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
|
|
||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||||
@ -102,9 +101,11 @@ public class SecurityEvaluationContextExtensionTests {
|
|||||||
public void setTrustResolverWhenNotNullThenVerifyRootObject() {
|
public void setTrustResolverWhenNotNullThenVerifyRootObject() {
|
||||||
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
|
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
|
||||||
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
|
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
|
||||||
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
AuthenticationTrustResolver trustResolver = mock(AuthenticationTrustResolver.class);
|
||||||
|
given(trustResolver.isAuthenticated(explicit)).willReturn(true);
|
||||||
this.securityExtension.setTrustResolver(trustResolver);
|
this.securityExtension.setTrustResolver(trustResolver);
|
||||||
assertThat(getRoot()).extracting("trustResolver").isEqualTo(trustResolver);
|
assertThat(getRoot().isAuthenticated()).isTrue();
|
||||||
|
verify(trustResolver).isAuthenticated(explicit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -117,11 +118,11 @@ public class SecurityEvaluationContextExtensionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setRoleHierarchyWhenNotNullThenVerifyRootObject() {
|
public void setRoleHierarchyWhenNotNullThenVerifyRootObject() {
|
||||||
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
|
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_PARENT");
|
||||||
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
|
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
|
||||||
RoleHierarchy roleHierarchy = new NullRoleHierarchy();
|
RoleHierarchy roleHierarchy = RoleHierarchyImpl.fromHierarchy("ROLE_PARENT > ROLE_EXPLICIT");
|
||||||
this.securityExtension.setRoleHierarchy(roleHierarchy);
|
this.securityExtension.setRoleHierarchy(roleHierarchy);
|
||||||
assertThat(getRoot()).extracting("roleHierarchy").isEqualTo(roleHierarchy);
|
assertThat(getRoot().hasRole("EXPLICIT")).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -143,25 +144,25 @@ public class SecurityEvaluationContextExtensionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setDefaultRolePrefixWhenCustomThenVerifyRootObject() {
|
public void setDefaultRolePrefixWhenCustomThenVerifyRootObject() {
|
||||||
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
|
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "CUSTOM_EXPLICIT");
|
||||||
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
|
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
|
||||||
String defaultRolePrefix = "CUSTOM_";
|
String defaultRolePrefix = "CUSTOM_";
|
||||||
this.securityExtension.setDefaultRolePrefix(defaultRolePrefix);
|
this.securityExtension.setDefaultRolePrefix(defaultRolePrefix);
|
||||||
assertThat(getRoot()).extracting("defaultRolePrefix").isEqualTo(defaultRolePrefix);
|
assertThat(getRoot().hasRole("EXPLICIT")).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getRootObjectWhenAdditionalFieldsNotSetThenVerifyDefaults() {
|
public void getRootObjectWhenAdditionalFieldsNotSetThenVerifyDefaults() {
|
||||||
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
|
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
|
||||||
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
|
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
|
||||||
SecurityExpressionRoot root = getRoot();
|
SecurityExpressionRoot<?> securityExpressionRoot = getRoot();
|
||||||
assertThat(root).extracting("trustResolver").isInstanceOf(AuthenticationTrustResolverImpl.class);
|
assertThat(securityExpressionRoot.isAuthenticated()).isTrue();
|
||||||
assertThat(root).extracting("roleHierarchy").isInstanceOf(NullRoleHierarchy.class);
|
assertThat(securityExpressionRoot.hasRole("PARENT")).isFalse();
|
||||||
assertThat(root).extracting("permissionEvaluator").isInstanceOf(DenyAllPermissionEvaluator.class);
|
assertThat(securityExpressionRoot.hasRole("EXPLICIT")).isTrue();
|
||||||
assertThat(root).extracting("defaultRolePrefix").isEqualTo("ROLE_");
|
assertThat(securityExpressionRoot.hasPermission(new Object(), "read")).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecurityExpressionRoot getRoot() {
|
private SecurityExpressionRoot<?> getRoot() {
|
||||||
return this.securityExtension.getRootObject();
|
return this.securityExtension.getRootObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +144,43 @@ Another manager is the `AuthenticatedAuthorizationManager`.
|
|||||||
It can be used to differentiate between anonymous, fully-authenticated and remember-me authenticated users.
|
It can be used to differentiate between anonymous, fully-authenticated and remember-me authenticated users.
|
||||||
Many sites allow certain limited access under remember-me authentication, but require a user to confirm their identity by logging in for full access.
|
Many sites allow certain limited access under remember-me authentication, but require a user to confirm their identity by logging in for full access.
|
||||||
|
|
||||||
|
[[authz-authorization-manager-factory]]
|
||||||
|
=== Creating AuthorizationManager instances
|
||||||
|
|
||||||
|
The javadoc:org.springframework.security.authorization.AuthorizationManagerFactory[] interface (introduced in Spring Security 7.0) is used to create generic ``AuthorizationManager``s in xref:servlet/authorization/authorize-http-requests.adoc[request-based] and xref:servlet/authorization/method-security.adoc[method-based] authorization components.
|
||||||
|
The following is a sketch of the `AuthorizationManagerFactory` interface:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
public interface AuthorizationManagerFactory<T> {
|
||||||
|
AuthorizationManager<T> permitAll();
|
||||||
|
AuthorizationManager<T> denyAll();
|
||||||
|
AuthorizationManager<T> hasRole(String role);
|
||||||
|
AuthorizationManager<T> hasAnyRole(String... roles);
|
||||||
|
AuthorizationManager<T> hasAuthority(String authority);
|
||||||
|
AuthorizationManager<T> hasAnyAuthority(String... authorities);
|
||||||
|
AuthorizationManager<T> authenticated();
|
||||||
|
AuthorizationManager<T> fullyAuthenticated();
|
||||||
|
AuthorizationManager<T> rememberMe();
|
||||||
|
AuthorizationManager<T> anonymous();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
The default implementation is javadoc:org.springframework.security.authorization.DefaultAuthorizationManagerFactory[], which allows for customizing the `rolePrefix` (defaults to `"ROLE_"`), `RoleHierarchy` and `AuthenticationTrustManager` that are provided to the ``AuthorizationManager``s created by the factory.
|
||||||
|
|
||||||
|
In order to customize the default instance used by Spring Security, simply publish a bean as in the following example:
|
||||||
|
|
||||||
|
include-code::./AuthorizationManagerFactoryConfiguration[tag=config,indent=0]
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
It is also possible to target a specific usage of this factory within Spring Security by providing a concrete parameterized type instead of a generic type.
|
||||||
|
See examples of each in the xref:servlet/authorization/authorize-http-requests.adoc#customizing-authorization-managers[request-based] and xref:servlet/authorization/method-security.adoc#customizing-authorization-managers[method-based] sections of the documentation.
|
||||||
|
|
||||||
|
In addition to simply customizing the default instance of `AuthorizationManagerFactory`, you can provide your own implementation to fully customize the instances created by the factory and provide your own implementations.
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
The {gh-url}/core/src/main/java/org/springframework/security/authorization/AuthorizationManagerFactory.java[actual interface] provides default implementations for all factory methods, which allows custom implementations to only implement the methods that need to be customized.
|
||||||
|
|
||||||
[[authz-authorization-managers]]
|
[[authz-authorization-managers]]
|
||||||
==== AuthorizationManagers
|
==== AuthorizationManagers
|
||||||
There are also helpful static factories in javadoc:org.springframework.security.authorization.AuthorizationManagers[] for composing individual ``AuthorizationManager``s into more sophisticated expressions.
|
There are also helpful static factories in javadoc:org.springframework.security.authorization.AuthorizationManagers[] for composing individual ``AuthorizationManager``s into more sophisticated expressions.
|
||||||
|
@ -57,6 +57,7 @@ In many cases, your authorization rules will be more sophisticated than that, so
|
|||||||
* I want to <<match-by-custom, match a request programmatically>>
|
* I want to <<match-by-custom, match a request programmatically>>
|
||||||
* I want to <<authorize-requests, authorize a request programmatically>>
|
* I want to <<authorize-requests, authorize a request programmatically>>
|
||||||
* I want to <<remote-authorization-manager, delegate request authorization>> to a policy agent
|
* I want to <<remote-authorization-manager, delegate request authorization>> to a policy agent
|
||||||
|
* I want to <<customizing-authorization-managers,customize how authorization managers are created>>
|
||||||
|
|
||||||
[[request-authorization-architecture]]
|
[[request-authorization-architecture]]
|
||||||
== Understanding How Request Authorization Components Work
|
== Understanding How Request Authorization Components Work
|
||||||
@ -765,6 +766,24 @@ You will notice that since we are using the `hasRole` expression we do not need
|
|||||||
<6> Any URL that has not already been matched on is denied access.
|
<6> Any URL that has not already been matched on is denied access.
|
||||||
This is a good strategy if you do not want to accidentally forget to update your authorization rules.
|
This is a good strategy if you do not want to accidentally forget to update your authorization rules.
|
||||||
|
|
||||||
|
[[customizing-authorization-managers]]
|
||||||
|
== Customizing Authorization Managers
|
||||||
|
|
||||||
|
When you use the `authorizeHttpRequests` DSL, Spring Security takes care of creating the appropriate `AuthorizationManager` instances for you.
|
||||||
|
In certain cases, you may want to customize what is created in order to have complete control over how authorization decisions are made xref:servlet/authorization/architecture.adoc#authz-delegate-authorization-manager[at the framework level].
|
||||||
|
|
||||||
|
In order to take control of creating instances of `AuthorizationManager` for authorizing HTTP requests, you can create a custom xref:servlet/authorization/architecture.adoc#authz-authorization-manager-factory[`AuthorizationManagerFactory`].
|
||||||
|
For example, let's say you want to create a convention that authenticated users must be authenticated _AND_ have the `USER` role.
|
||||||
|
To do this, you can create a custom implementation for HTTP requests as in the following example:
|
||||||
|
|
||||||
|
include-code::./CustomHttpRequestsAuthorizationManagerFactory[tag=class,indent=0]
|
||||||
|
|
||||||
|
Now, whenever you <<activate-request-security,require authentication>>, Spring Security will automatically invoke your custom factory to create an instance of `AuthorizationManager` that requires authentication _AND_ the `USER` role.
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
We use this as a simple example of creating a custom `AuthorizationManagerFactory`, though it is also possible (and often simpler) to replace a specific `AuthorizationManager` only for a particular request.
|
||||||
|
See <<remote-authorization-manager>> for an example.
|
||||||
|
|
||||||
[[authorization-expressions]]
|
[[authorization-expressions]]
|
||||||
== Expressing Authorization with SpEL
|
== Expressing Authorization with SpEL
|
||||||
|
|
||||||
|
@ -1995,6 +1995,24 @@ This works on both classes and interfaces.
|
|||||||
This does not work for interfaces, since they do not have debug information about the parameter names.
|
This does not work for interfaces, since they do not have debug information about the parameter names.
|
||||||
For interfaces, either annotations or the `-parameters` approach must be used.
|
For interfaces, either annotations or the `-parameters` approach must be used.
|
||||||
|
|
||||||
|
[[customizing-authorization-managers]]
|
||||||
|
== Customizing Authorization Managers
|
||||||
|
|
||||||
|
When you use SpEL expressions with <<use-preauthorize,`@PreAuthorize`>>, <<use-postauthorize,`@PostAuthorize`>>, <<use-prefilter,`@PreFilter`>> and <<use-postfilter,`@PostFilter`>>, Spring Security takes care of creating the appropriate `AuthorizationManager` instances for you.
|
||||||
|
In certain cases, you may want to customize what is created in order to have complete control over how authorization decisions are made xref:servlet/authorization/architecture.adoc#authz-delegate-authorization-manager[at the framework level].
|
||||||
|
|
||||||
|
In order to take control of creating instances of `AuthorizationManager` for pre- and post-annotations, you can create a custom xref:servlet/authorization/architecture.adoc#authz-authorization-manager-factory[`AuthorizationManagerFactory`].
|
||||||
|
For example, let's say you want to allow users with the `ADMIN` role whenever any other role is required.
|
||||||
|
To do this, you can create a custom implementation for method security as in the following example:
|
||||||
|
|
||||||
|
include-code::./CustomMethodInvocationAuthorizationManagerFactory[tag=class,indent=0]
|
||||||
|
|
||||||
|
Now, whenever you <<use-preauthorize,use the `@PreAuthorize` annotation>> with `hasRole` or `hasAnyRole`, Spring Security will automatically invoke your custom factory to create an instance of `AuthorizationManager` that allows access for the given role(s) _OR_ the `ADMIN` role.
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
We use this as a simple example of creating a custom `AuthorizationManagerFactory`, though the same outcome could be accomplished with <<favor-granting-authorities,a role hierarchy>>.
|
||||||
|
Use whichever approach fits best in your situation.
|
||||||
|
|
||||||
[[authorize-object]]
|
[[authorize-object]]
|
||||||
== Authorizing Arbitrary Objects
|
== Authorizing Arbitrary Objects
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ Each section that follows will indicate the more notable removals as well as the
|
|||||||
== Core
|
== Core
|
||||||
|
|
||||||
* Removed `AuthorizationManager#check` in favor of `AuthorizationManager#authorize`
|
* Removed `AuthorizationManager#check` in favor of `AuthorizationManager#authorize`
|
||||||
|
* Added xref:servlet/authorization/architecture.adoc#authz-authorization-manager-factory[`AuthorizationManagerFactory`] for creating `AuthorizationManager` instances in xref:servlet/authorization/authorize-http-requests.adoc#customizing-authorization-managers[request-based] and xref:servlet/authorization/method-security.adoc#customizing-authorization-managers[method-based] authorization components
|
||||||
|
|
||||||
== Config
|
== Config
|
||||||
|
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain clients copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.docs.servlet.authorization.authzauthorizationmanagerfactory;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
|
||||||
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
|
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Documentation for {@link AuthorizationManagerFactory}.
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
public class AuthorizationManagerFactoryConfiguration {
|
||||||
|
|
||||||
|
// tag::config[]
|
||||||
|
@Bean
|
||||||
|
<T> AuthorizationManagerFactory<T> authorizationManagerFactory() {
|
||||||
|
DefaultAuthorizationManagerFactory<T> authorizationManagerFactory =
|
||||||
|
new DefaultAuthorizationManagerFactory<>();
|
||||||
|
authorizationManagerFactory.setTrustResolver(getAuthenticationTrustResolver());
|
||||||
|
authorizationManagerFactory.setRoleHierarchy(getRoleHierarchy());
|
||||||
|
authorizationManagerFactory.setRolePrefix("role_");
|
||||||
|
|
||||||
|
return authorizationManagerFactory;
|
||||||
|
}
|
||||||
|
// end::config[]
|
||||||
|
|
||||||
|
private static AuthenticationTrustResolverImpl getAuthenticationTrustResolver() {
|
||||||
|
AuthenticationTrustResolverImpl authenticationTrustResolver =
|
||||||
|
new AuthenticationTrustResolverImpl();
|
||||||
|
authenticationTrustResolver.setAnonymousClass(Anonymous.class);
|
||||||
|
authenticationTrustResolver.setRememberMeClass(RememberMe.class);
|
||||||
|
|
||||||
|
return authenticationTrustResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RoleHierarchyImpl getRoleHierarchy() {
|
||||||
|
return RoleHierarchyImpl.fromHierarchy("role_admin > role_user");
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Anonymous extends TestingAuthenticationToken {
|
||||||
|
|
||||||
|
Anonymous(String principal) {
|
||||||
|
super(principal, "", "role_anonymous");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RememberMe extends TestingAuthenticationToken {
|
||||||
|
|
||||||
|
RememberMe(String principal) {
|
||||||
|
super(principal, "", "role_rememberMe");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain clients copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.docs.servlet.authorization.authzauthorizationmanagerfactory;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.docs.servlet.authorization.authzauthorizationmanagerfactory.AuthorizationManagerFactoryConfiguration.Anonymous;
|
||||||
|
import org.springframework.security.docs.servlet.authorization.authzauthorizationmanagerfactory.AuthorizationManagerFactoryConfiguration.RememberMe;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||||
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link AuthorizationManagerFactoryConfiguration}.
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
@ExtendWith(SpringTestContextExtension.class)
|
||||||
|
public class AuthorizationManagerFactoryConfigurationTests {
|
||||||
|
|
||||||
|
public final SpringTestContext spring = new SpringTestContext(this);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MockMvc mockMvc;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAnonymousWhenCustomAnonymousClassThenOk() throws Exception {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,
|
||||||
|
TestController.class).autowire();
|
||||||
|
Authentication authentication = new Anonymous("anonymous");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/anonymous").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getAnonymousWhenAuthenticatedThenForbidden() throws Exception {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,
|
||||||
|
TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", "role_user");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/anonymous").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getRememberMeWhenCustomRememberMeClassThenOk() throws Exception {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,
|
||||||
|
TestController.class).autowire();
|
||||||
|
Authentication authentication = new RememberMe("rememberMe");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/rememberMe").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getRememberMeWhenAuthenticatedThenForbidden() throws Exception {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,
|
||||||
|
TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", "role_user");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/rememberMe").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUserWhenCustomUserRoleThenOk() throws Exception {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,
|
||||||
|
TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", "role_user");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUserWhenCustomAdminRoleThenOk() throws Exception {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,
|
||||||
|
TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("admin", "", "role_admin");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPreAuthorizeWhenCustomUserRoleThenOk() throws Exception {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,
|
||||||
|
TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", "role_user");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/preAuthorize").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPreAuthorizeWhenCustomAdminRoleThenOk() throws Exception {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,
|
||||||
|
TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("admin", "", "role_admin");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/preAuthorize").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPreAuthorizeWhenOtherRoleThenForbidden() throws Exception {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,
|
||||||
|
TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("other", "", "role_other");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/preAuthorize").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebMvc
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableMethodSecurity
|
||||||
|
@Configuration
|
||||||
|
static class SecurityConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize
|
||||||
|
.requestMatchers("/anonymous").anonymous()
|
||||||
|
.requestMatchers("/rememberMe").rememberMe()
|
||||||
|
.requestMatchers("/user").hasRole("user")
|
||||||
|
.requestMatchers("/preAuthorize").permitAll()
|
||||||
|
.anyRequest().denyAll()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class TestController {
|
||||||
|
|
||||||
|
@GetMapping({ "/anonymous", "/rememberMe", "/user" })
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
void httpRequest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/preAuthorize")
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
@PreAuthorize("hasRole('user')")
|
||||||
|
void preAuthorize() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain clients copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.docs.servlet.authorization.customizingauthorizationmanagers;
|
||||||
|
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagers;
|
||||||
|
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||||
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Documentation for {@link AuthorizationManagerFactory}.
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
// tag::class[]
|
||||||
|
@Component
|
||||||
|
public class CustomHttpRequestsAuthorizationManagerFactory
|
||||||
|
implements AuthorizationManagerFactory<RequestAuthorizationContext> {
|
||||||
|
|
||||||
|
private final AuthorizationManagerFactory<RequestAuthorizationContext> delegate =
|
||||||
|
new DefaultAuthorizationManagerFactory<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<RequestAuthorizationContext> authenticated() {
|
||||||
|
return AuthorizationManagers.allOf(
|
||||||
|
this.delegate.authenticated(),
|
||||||
|
this.delegate.hasRole("USER")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// end::class[]
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain clients copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.docs.servlet.authorization.customizingauthorizationmanagers;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||||
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||||
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link CustomHttpRequestsAuthorizationManagerFactory}.
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
@ExtendWith(SpringTestContextExtension.class)
|
||||||
|
public class CustomHttpRequestsAuthorizationManagerFactoryTests {
|
||||||
|
|
||||||
|
public final SpringTestContext spring = new SpringTestContext(this);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MockMvc mockMvc;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getHelloWhenAnonymousThenForbidden() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/hello").with(anonymous()))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(unauthenticated());
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getHelloWhenAuthenticatedWithNoRolesThenForbidden() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", Collections.emptyList());
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/hello").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getHelloWhenAuthenticatedWithUserRoleThenOk() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", "ROLE_USER");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/hello").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getHelloWhenAuthenticatedWithOtherRoleThenForbidden() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", "ROLE_OTHER");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/hello").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebMvc
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
static class SecurityConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
CustomHttpRequestsAuthorizationManagerFactory customHttpRequestsAuthorizationManagerFactory() {
|
||||||
|
return new CustomHttpRequestsAuthorizationManagerFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class TestController {
|
||||||
|
|
||||||
|
@GetMapping("/**")
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
void ok() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain clients copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.docs.servlet.authorization.customizingauthorizationmanagers;
|
||||||
|
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagers;
|
||||||
|
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Documentation for {@link AuthorizationManagerFactory}.
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
// tag::class[]
|
||||||
|
@Component
|
||||||
|
public class CustomMethodInvocationAuthorizationManagerFactory
|
||||||
|
implements AuthorizationManagerFactory<MethodInvocation> {
|
||||||
|
|
||||||
|
private final AuthorizationManagerFactory<MethodInvocation> delegate =
|
||||||
|
new DefaultAuthorizationManagerFactory<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<MethodInvocation> hasRole(String role) {
|
||||||
|
return AuthorizationManagers.anyOf(
|
||||||
|
this.delegate.hasRole(role),
|
||||||
|
this.delegate.hasRole("ADMIN")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorizationManager<MethodInvocation> hasAnyRole(String... roles) {
|
||||||
|
return AuthorizationManagers.anyOf(
|
||||||
|
this.delegate.hasAnyRole(roles),
|
||||||
|
this.delegate.hasRole("ADMIN")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// end::class[]
|
@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain clients copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.docs.servlet.authorization.customizingauthorizationmanagers;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||||
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||||
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link CustomMethodInvocationAuthorizationManagerFactory}.
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
@ExtendWith(SpringTestContextExtension.class)
|
||||||
|
public class CustomMethodInvocationAuthorizationManagerFactoryTests {
|
||||||
|
|
||||||
|
public final SpringTestContext spring = new SpringTestContext(this);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MockMvc mockMvc;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUserWhenAnonymousThenForbidden() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/user").with(anonymous()))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(unauthenticated());
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUserWhenAuthenticatedWithNoRolesThenForbidden() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", Collections.emptyList());
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUserWhenAuthenticatedWithUserRoleThenOk() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", "ROLE_USER");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUserWhenAuthenticatedWithAdminRoleThenOk() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("admin", "", "ROLE_ADMIN");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUserWhenAuthenticatedWithOtherRoleThenForbidden() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", "ROLE_OTHER");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getRolesWhenAuthenticatedWithRole1RoleThenOk() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", "ROLE_ROLE1");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/roles").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getRolesWhenAuthenticatedWithAdminRoleThenOk() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("admin", "", "ROLE_ADMIN");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/roles").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getRolesWhenAuthenticatedWithOtherRoleThenForbidden() throws Exception {
|
||||||
|
this.spring.register(SecurityConfiguration.class, TestController.class).autowire();
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "", "ROLE_OTHER");
|
||||||
|
// @formatter:off
|
||||||
|
this.mockMvc.perform(get("/roles").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebMvc
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableMethodSecurity
|
||||||
|
@Configuration
|
||||||
|
static class SecurityConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
CustomMethodInvocationAuthorizationManagerFactory customMethodInvocationAuthorizationManagerFactory() {
|
||||||
|
return new CustomMethodInvocationAuthorizationManagerFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class TestController {
|
||||||
|
|
||||||
|
@GetMapping("/user")
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
@PreAuthorize("hasRole('USER')")
|
||||||
|
void user() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/roles")
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
@PreAuthorize("hasAnyRole('ROLE1', 'ROLE2')")
|
||||||
|
void roles() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kt.docs.servlet.authorization.authzauthorizationmanagerfactory
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl
|
||||||
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory
|
||||||
|
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Documentation for [org.springframework.security.authorization.AuthorizationManagerFactory].
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
class AuthorizationManagerFactoryConfiguration {
|
||||||
|
// tag::config[]
|
||||||
|
@Bean
|
||||||
|
fun <T> authorizationManagerFactory(): AuthorizationManagerFactory<T> {
|
||||||
|
val authorizationManagerFactory = DefaultAuthorizationManagerFactory<T>()
|
||||||
|
authorizationManagerFactory.setTrustResolver(getAuthenticationTrustResolver())
|
||||||
|
authorizationManagerFactory.setRoleHierarchy(getRoleHierarchy())
|
||||||
|
authorizationManagerFactory.setRolePrefix("role_")
|
||||||
|
|
||||||
|
return authorizationManagerFactory
|
||||||
|
}
|
||||||
|
// end::config[]
|
||||||
|
|
||||||
|
private fun getAuthenticationTrustResolver(): AuthenticationTrustResolverImpl {
|
||||||
|
val authenticationTrustResolver = AuthenticationTrustResolverImpl()
|
||||||
|
authenticationTrustResolver.setAnonymousClass(Anonymous::class.java)
|
||||||
|
authenticationTrustResolver.setRememberMeClass(RememberMe::class.java)
|
||||||
|
|
||||||
|
return authenticationTrustResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRoleHierarchy(): RoleHierarchyImpl {
|
||||||
|
return RoleHierarchyImpl.fromHierarchy("role_admin > role_user")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Anonymous(principal: String) :
|
||||||
|
TestingAuthenticationToken(principal, "", "role_anonymous")
|
||||||
|
|
||||||
|
internal class RememberMe(principal: String) :
|
||||||
|
TestingAuthenticationToken(principal, "", "role_rememberMe")
|
||||||
|
}
|
@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kt.docs.servlet.authorization.authzauthorizationmanagerfactory
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.security.config.test.SpringTestContext
|
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension
|
||||||
|
import org.springframework.security.kt.docs.servlet.authorization.authzauthorizationmanagerfactory.AuthorizationManagerFactoryConfiguration.Anonymous
|
||||||
|
import org.springframework.security.kt.docs.servlet.authorization.authzauthorizationmanagerfactory.AuthorizationManagerFactoryConfiguration.RememberMe
|
||||||
|
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication
|
||||||
|
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated
|
||||||
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for [AuthorizationManagerFactoryConfiguration].
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
@ExtendWith(SpringTestContextExtension::class)
|
||||||
|
class AuthorizationManagerFactoryConfigurationTests {
|
||||||
|
@JvmField
|
||||||
|
val spring = SpringTestContext(this)
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
lateinit var mockMvc: MockMvc
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getAnonymousWhenCustomAnonymousClassThenOk() {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)
|
||||||
|
.autowire()
|
||||||
|
val authentication = Anonymous("anonymous")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/anonymous").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getAnonymousWhenAuthenticatedThenForbidden() {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)
|
||||||
|
.autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "role_user")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/anonymous").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getRememberMeWhenCustomRememberMeClassThenOk() {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)
|
||||||
|
.autowire()
|
||||||
|
val authentication = RememberMe("rememberMe")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/rememberMe").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getRememberMeWhenAuthenticatedThenForbidden() {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)
|
||||||
|
.autowire()
|
||||||
|
val user = TestingAuthenticationToken("user", "", "role_user")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/rememberMe").with(authentication(user)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(user))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getUserWhenCustomUserRoleThenOk() {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)
|
||||||
|
.autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "role_user")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getUserWhenCustomAdminRoleThenOk() {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)
|
||||||
|
.autowire()
|
||||||
|
val admin = TestingAuthenticationToken("admin", "", "role_admin")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/user").with(authentication(admin)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(admin))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getPreAuthorizeWhenCustomUserRoleThenOk() {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)
|
||||||
|
.autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "role_user")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/preAuthorize").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getPreAuthorizeWhenCustomAdminRoleThenOk() {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)
|
||||||
|
.autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("admin", "", "role_admin")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/preAuthorize").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getPreAuthorizeWhenOtherRoleThenForbidden() {
|
||||||
|
this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)
|
||||||
|
.autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("other", "", "role_other")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/preAuthorize").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebMvc
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableMethodSecurity
|
||||||
|
@Configuration
|
||||||
|
internal open class SecurityConfiguration {
|
||||||
|
@Bean
|
||||||
|
@Throws(Exception::class)
|
||||||
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
// @formatter:off
|
||||||
|
http.authorizeHttpRequests { authorize ->
|
||||||
|
authorize
|
||||||
|
.requestMatchers("/anonymous").anonymous()
|
||||||
|
.requestMatchers("/rememberMe").rememberMe()
|
||||||
|
.requestMatchers("/user").hasRole("user")
|
||||||
|
.requestMatchers("/preAuthorize").permitAll()
|
||||||
|
.anyRequest().denyAll()
|
||||||
|
}
|
||||||
|
// @formatter:on
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun testController(testService: TestService): TestController {
|
||||||
|
return TestController(testService())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun testService(): TestService {
|
||||||
|
return TestServiceImpl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
internal open class TestController(private val testService: TestService) {
|
||||||
|
@GetMapping(value = ["/anonymous", "/rememberMe", "/user"])
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
fun httpRequest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/preAuthorize")
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
fun preAuthorize() {
|
||||||
|
testService.preAuthorize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface TestService {
|
||||||
|
@PreAuthorize("hasRole('user')")
|
||||||
|
fun preAuthorize()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal open class TestServiceImpl : TestService {
|
||||||
|
override fun preAuthorize() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kt.docs.servlet.authorization.customizingauthorizationmanagers
|
||||||
|
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagers
|
||||||
|
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory
|
||||||
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Documentation for {@link AuthorizationManagerFactory}.
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
// tag::class[]
|
||||||
|
@Component
|
||||||
|
class CustomHttpRequestsAuthorizationManagerFactory : AuthorizationManagerFactory<RequestAuthorizationContext> {
|
||||||
|
private val delegate = DefaultAuthorizationManagerFactory<RequestAuthorizationContext>()
|
||||||
|
|
||||||
|
override fun authenticated(): AuthorizationManager<RequestAuthorizationContext> {
|
||||||
|
return AuthorizationManagers.allOf(
|
||||||
|
delegate.authenticated(),
|
||||||
|
delegate.hasRole("USER")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end::class[]
|
||||||
|
|
||||||
|
|
@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kt.docs.servlet.authorization.customizingauthorizationmanagers
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.security.config.test.SpringTestContext
|
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension
|
||||||
|
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous
|
||||||
|
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication
|
||||||
|
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers
|
||||||
|
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated
|
||||||
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for [CustomHttpRequestsAuthorizationManagerFactory].
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
@ExtendWith(SpringTestContextExtension::class)
|
||||||
|
class CustomHttpRequestsAuthorizationManagerFactoryTests {
|
||||||
|
@JvmField
|
||||||
|
val spring = SpringTestContext(this)
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
lateinit var mockMvc: MockMvc
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getHelloWhenAnonymousThenForbidden() {
|
||||||
|
spring.register(SecurityConfiguration::class.java, TestController::class.java).autowire()
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/hello").with(anonymous()))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(SecurityMockMvcResultMatchers.unauthenticated())
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getHelloWhenAuthenticatedWithUserRoleThenOk() {
|
||||||
|
spring.register(SecurityConfiguration::class.java, TestController::class.java).autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "ROLE_USER")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/hello").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getHelloWhenAuthenticatedWithOtherRoleThenForbidden() {
|
||||||
|
spring.register(SecurityConfiguration::class.java, TestController::class.java).autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "ROLE_OTHER")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/hello").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getHelloWhenAuthenticatedWithNoRolesThenForbidden() {
|
||||||
|
spring.register(SecurityConfiguration::class.java, TestController::class.java).autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", listOf())
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/hello").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebMvc
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
internal open class SecurityConfiguration {
|
||||||
|
@Bean
|
||||||
|
@Throws(Exception::class)
|
||||||
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests { authorize ->
|
||||||
|
authorize.anyRequest().authenticated()
|
||||||
|
}
|
||||||
|
// @formatter:on
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun customHttpRequestsAuthorizationManagerFactory(): CustomHttpRequestsAuthorizationManagerFactory {
|
||||||
|
return CustomHttpRequestsAuthorizationManagerFactory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
internal class TestController {
|
||||||
|
@GetMapping("/**")
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
fun ok() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kt.docs.servlet.authorization.customizingauthorizationmanagers
|
||||||
|
|
||||||
|
import org.aopalliance.intercept.MethodInvocation
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagers
|
||||||
|
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Documentation for [AuthorizationManagerFactory].
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
// tag::class[]
|
||||||
|
@Component
|
||||||
|
class CustomMethodInvocationAuthorizationManagerFactory : AuthorizationManagerFactory<MethodInvocation> {
|
||||||
|
private val delegate = DefaultAuthorizationManagerFactory<MethodInvocation>()
|
||||||
|
|
||||||
|
override fun hasRole(role: String): AuthorizationManager<MethodInvocation> {
|
||||||
|
return AuthorizationManagers.anyOf(
|
||||||
|
delegate.hasRole(role),
|
||||||
|
delegate.hasRole("ADMIN")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasAnyRole(vararg roles: String): AuthorizationManager<MethodInvocation> {
|
||||||
|
return AuthorizationManagers.anyOf(
|
||||||
|
delegate.hasAnyRole(*roles),
|
||||||
|
delegate.hasRole("ADMIN")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end::class[]
|
||||||
|
|
@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.kt.docs.servlet.authorization.customizingauthorizationmanagers
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.security.config.test.SpringTestContext
|
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension
|
||||||
|
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous
|
||||||
|
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication
|
||||||
|
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated
|
||||||
|
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated
|
||||||
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for [CustomMethodInvocationAuthorizationManagerFactory].
|
||||||
|
*
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
*/
|
||||||
|
@ExtendWith(SpringTestContextExtension::class)
|
||||||
|
class CustomMethodInvocationAuthorizationManagerFactoryTests {
|
||||||
|
@JvmField
|
||||||
|
val spring = SpringTestContext(this)
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
lateinit var mockMvc: MockMvc
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getUserWhenAnonymousThenForbidden() {
|
||||||
|
spring.register(SecurityConfiguration::class.java).autowire()
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/user").with(anonymous()))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(unauthenticated())
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getUserWhenAuthenticatedWithNoRolesThenForbidden() {
|
||||||
|
spring.register(SecurityConfiguration::class.java).autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", listOf())
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getUserWhenAuthenticatedWithUserRoleThenOk() {
|
||||||
|
spring.register(SecurityConfiguration::class.java).autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "ROLE_USER")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getUserWhenAuthenticatedWithAdminRoleThenOk() {
|
||||||
|
spring.register(SecurityConfiguration::class.java).autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "ROLE_ADMIN")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getUserWhenAuthenticatedWithOtherRoleThenForbidden() {
|
||||||
|
spring.register(SecurityConfiguration::class.java).autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "ROLE_OTHER")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/user").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getRolesWhenAuthenticatedWithRole1RoleThenOk() {
|
||||||
|
spring.register(SecurityConfiguration::class.java).autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "ROLE_ROLE1")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/roles").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getRolesWhenAuthenticatedWithAdminRoleThenOk() {
|
||||||
|
spring.register(SecurityConfiguration::class.java).autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "ROLE_ADMIN")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/roles").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getRolesWhenAuthenticatedWithOtherRoleThenForbidden() {
|
||||||
|
spring.register(SecurityConfiguration::class.java).autowire()
|
||||||
|
val authentication = TestingAuthenticationToken("user", "", "ROLE_OTHER")
|
||||||
|
// @formatter:off
|
||||||
|
mockMvc.perform(get("/roles").with(authentication(authentication)))
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(authenticated().withAuthentication(authentication))
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebMvc
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableMethodSecurity
|
||||||
|
@Configuration
|
||||||
|
internal open class SecurityConfiguration {
|
||||||
|
@Bean
|
||||||
|
@Throws(Exception::class)
|
||||||
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests { authorize ->
|
||||||
|
authorize.anyRequest().authenticated()
|
||||||
|
}
|
||||||
|
// @formatter:on
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun customMethodInvocationAuthorizationManagerFactory(): CustomMethodInvocationAuthorizationManagerFactory {
|
||||||
|
return CustomMethodInvocationAuthorizationManagerFactory()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun testController(testService: TestService): TestController {
|
||||||
|
return TestController(testService())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun testService(): TestService {
|
||||||
|
return TestServiceImpl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
internal open class TestController(private val testService: TestService) {
|
||||||
|
@GetMapping("/user")
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
fun user() {
|
||||||
|
testService.user()
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/roles")
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
fun roles() {
|
||||||
|
testService.roles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface TestService {
|
||||||
|
@PreAuthorize("hasRole('USER')")
|
||||||
|
fun user()
|
||||||
|
|
||||||
|
@PreAuthorize("hasAnyRole('ROLE1', 'ROLE2')")
|
||||||
|
fun roles()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal open class TestServiceImpl : TestService {
|
||||||
|
override fun user() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun roles() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2004-present the original author or authors.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="
|
||||||
|
http://www.springframework.org/schema/beans
|
||||||
|
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<!-- tag::config[] -->
|
||||||
|
<b:bean id="authorizationManagerFactory" class="org.springframework.security.authorization.DefaultAuthorizationManagerFactory">
|
||||||
|
<b:property name="trustResolver" ref="authenticationTrustResolver"/>
|
||||||
|
<b:property name="roleHierarchy" ref="roleHierarchy"/>
|
||||||
|
<b:property name="rolePrefix" value="role_"/>
|
||||||
|
</b:bean>
|
||||||
|
<!-- end::config[] -->
|
||||||
|
|
||||||
|
<b:bean id="authenticationTrustResolver" class="org.springframework.security.authentication.AuthenticationTrustResolverImpl">
|
||||||
|
<b:property name="anonymousClass" value="org.springframework.security.authentication.TestingAuthenticationToken"/>
|
||||||
|
</b:bean>
|
||||||
|
|
||||||
|
<b:bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl" factory-method="fromHierarchy">
|
||||||
|
<b:constructor-arg name="hierarchy" value="role_admin > role_user"/>
|
||||||
|
</b:bean>
|
||||||
|
|
||||||
|
</b:beans>
|
@ -36,6 +36,7 @@
|
|||||||
<suppress files="JoseHeader\.java" checks="SpringMethodVisibility"/>
|
<suppress files="JoseHeader\.java" checks="SpringMethodVisibility"/>
|
||||||
<suppress files="DefaultLoginPageGeneratingFilterTests\.java" checks="SpringLeadingWhitespace"/>
|
<suppress files="DefaultLoginPageGeneratingFilterTests\.java" checks="SpringLeadingWhitespace"/>
|
||||||
<suppress files="AuthenticationException\.java" checks="MutableException"/>
|
<suppress files="AuthenticationException\.java" checks="MutableException"/>
|
||||||
|
<suppress files="FilterInvocationExpressionRoot\.java" checks="SpringMethodVisibility"/>
|
||||||
|
|
||||||
<!-- Lambdas that we can't replace with a method reference because a closure is required -->
|
<!-- Lambdas that we can't replace with a method reference because a closure is required -->
|
||||||
<suppress files="BearerTokenAuthenticationFilter\.java" checks="SpringLambda"/>
|
<suppress files="BearerTokenAuthenticationFilter\.java" checks="SpringLambda"/>
|
||||||
|
@ -28,9 +28,8 @@ import org.springframework.security.access.expression.AbstractSecurityExpression
|
|||||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||||
import org.springframework.security.access.expression.SecurityExpressionOperations;
|
import org.springframework.security.access.expression.SecurityExpressionOperations;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default implementation of {@link SecurityExpressionHandler} which uses a
|
* The default implementation of {@link SecurityExpressionHandler} which uses a
|
||||||
@ -43,12 +42,10 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public class DefaultMessageSecurityExpressionHandler<T> extends AbstractSecurityExpressionHandler<Message<T>> {
|
public class DefaultMessageSecurityExpressionHandler<T> extends AbstractSecurityExpressionHandler<Message<T>> {
|
||||||
|
|
||||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
|
public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
Message<T> message) {
|
Message<T> message) {
|
||||||
MessageSecurityExpressionRoot root = createSecurityExpressionRoot(authentication, message);
|
MessageSecurityExpressionRoot<T> root = createSecurityExpressionRoot(authentication, message);
|
||||||
StandardEvaluationContext ctx = new StandardEvaluationContext(root);
|
StandardEvaluationContext ctx = new StandardEvaluationContext(root);
|
||||||
BeanResolver beanResolver = getBeanResolver();
|
BeanResolver beanResolver = getBeanResolver();
|
||||||
if (beanResolver != null) {
|
if (beanResolver != null) {
|
||||||
@ -64,18 +61,21 @@ public class DefaultMessageSecurityExpressionHandler<T> extends AbstractSecurity
|
|||||||
return createSecurityExpressionRoot(() -> authentication, invocation);
|
return createSecurityExpressionRoot(() -> authentication, invocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessageSecurityExpressionRoot createSecurityExpressionRoot(
|
private MessageSecurityExpressionRoot<T> createSecurityExpressionRoot(
|
||||||
Supplier<? extends Authentication> authentication, Message<T> invocation) {
|
Supplier<? extends Authentication> authentication, Message<T> invocation) {
|
||||||
MessageSecurityExpressionRoot root = new MessageSecurityExpressionRoot(authentication, invocation);
|
MessageSecurityExpressionRoot<T> root = new MessageSecurityExpressionRoot<>(authentication, invocation);
|
||||||
|
root.setAuthorizationManagerFactory(getAuthorizationManagerFactory());
|
||||||
root.setPermissionEvaluator(getPermissionEvaluator());
|
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||||
root.setTrustResolver(this.trustResolver);
|
|
||||||
root.setRoleHierarchy(getRoleHierarchy());
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
||||||
Assert.notNull(trustResolver, "trustResolver cannot be null");
|
getDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);
|
||||||
this.trustResolver = trustResolver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ package org.springframework.security.messaging.access.expression;
|
|||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -27,13 +29,14 @@ import org.springframework.security.core.Authentication;
|
|||||||
*
|
*
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
* @author Evgeniy Cheban
|
* @author Evgeniy Cheban
|
||||||
|
* @author Steve Riesenberg
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
public class MessageSecurityExpressionRoot extends SecurityExpressionRoot {
|
public class MessageSecurityExpressionRoot<T> extends SecurityExpressionRoot<Message<T>> {
|
||||||
|
|
||||||
public final Message<?> message;
|
public final Message<T> message;
|
||||||
|
|
||||||
public MessageSecurityExpressionRoot(Authentication authentication, Message<?> message) {
|
public MessageSecurityExpressionRoot(Authentication authentication, Message<T> message) {
|
||||||
this(() -> authentication, message);
|
this(() -> authentication, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,8 +47,9 @@ public class MessageSecurityExpressionRoot extends SecurityExpressionRoot {
|
|||||||
* @param message the {@link Message} to use
|
* @param message the {@link Message} to use
|
||||||
* @since 5.8
|
* @since 5.8
|
||||||
*/
|
*/
|
||||||
public MessageSecurityExpressionRoot(Supplier<? extends Authentication> authentication, Message<?> message) {
|
public MessageSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
super(authentication);
|
Message<T> message) {
|
||||||
|
super(authentication, message);
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import org.springframework.security.access.expression.SecurityExpressionHandler;
|
|||||||
import org.springframework.security.access.expression.SecurityExpressionOperations;
|
import org.springframework.security.access.expression.SecurityExpressionOperations;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -36,14 +37,15 @@ import org.springframework.util.Assert;
|
|||||||
* create a {@link WebSecurityExpressionRoot}.
|
* create a {@link WebSecurityExpressionRoot}.
|
||||||
*
|
*
|
||||||
* @author Evgeniy Cheban
|
* @author Evgeniy Cheban
|
||||||
|
* @author Steve Riesenberg
|
||||||
* @since 5.8
|
* @since 5.8
|
||||||
*/
|
*/
|
||||||
public class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpressionHandler<RequestAuthorizationContext>
|
public class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpressionHandler<RequestAuthorizationContext>
|
||||||
implements SecurityExpressionHandler<RequestAuthorizationContext> {
|
implements SecurityExpressionHandler<RequestAuthorizationContext> {
|
||||||
|
|
||||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
|
||||||
|
|
||||||
private String defaultRolePrefix = "ROLE_";
|
private String defaultRolePrefix = DEFAULT_ROLE_PREFIX;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("NullAway") // https://github.com/spring-projects/spring-framework/issues/35371
|
@SuppressWarnings("NullAway") // https://github.com/spring-projects/spring-framework/issues/35371
|
||||||
@ -64,11 +66,13 @@ public class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpres
|
|||||||
|
|
||||||
private WebSecurityExpressionRoot createSecurityExpressionRoot(
|
private WebSecurityExpressionRoot createSecurityExpressionRoot(
|
||||||
Supplier<? extends @Nullable Authentication> authentication, RequestAuthorizationContext context) {
|
Supplier<? extends @Nullable Authentication> authentication, RequestAuthorizationContext context) {
|
||||||
WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, context.getRequest());
|
WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, context);
|
||||||
root.setRoleHierarchy(getRoleHierarchy());
|
root.setAuthorizationManagerFactory(getAuthorizationManagerFactory());
|
||||||
root.setPermissionEvaluator(getPermissionEvaluator());
|
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||||
root.setTrustResolver(this.trustResolver);
|
if (!DEFAULT_ROLE_PREFIX.equals(this.defaultRolePrefix)) {
|
||||||
|
// Ensure SecurityExpressionRoot can strip the custom role prefix
|
||||||
root.setDefaultRolePrefix(this.defaultRolePrefix);
|
root.setDefaultRolePrefix(this.defaultRolePrefix);
|
||||||
|
}
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,10 +80,12 @@ public class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpres
|
|||||||
* Sets the {@link AuthenticationTrustResolver} to be used. The default is
|
* Sets the {@link AuthenticationTrustResolver} to be used. The default is
|
||||||
* {@link AuthenticationTrustResolverImpl}.
|
* {@link AuthenticationTrustResolverImpl}.
|
||||||
* @param trustResolver the {@link AuthenticationTrustResolver} to use
|
* @param trustResolver the {@link AuthenticationTrustResolver} to use
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
||||||
Assert.notNull(trustResolver, "trustResolver cannot be null");
|
getDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);
|
||||||
this.trustResolver = trustResolver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,9 +97,13 @@ public class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpres
|
|||||||
* role ROLE_ADMIN will be used when the defaultRolePrefix is "ROLE_" (default).
|
* role ROLE_ADMIN will be used when the defaultRolePrefix is "ROLE_" (default).
|
||||||
* @param defaultRolePrefix the default prefix to add to roles. The default is
|
* @param defaultRolePrefix the default prefix to add to roles. The default is
|
||||||
* "ROLE_".
|
* "ROLE_".
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public void setDefaultRolePrefix(String defaultRolePrefix) {
|
public void setDefaultRolePrefix(String defaultRolePrefix) {
|
||||||
Assert.notNull(defaultRolePrefix, "defaultRolePrefix cannot be null");
|
Assert.notNull(defaultRolePrefix, "defaultRolePrefix cannot be null");
|
||||||
|
getDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);
|
||||||
this.defaultRolePrefix = defaultRolePrefix;
|
this.defaultRolePrefix = defaultRolePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,30 +23,33 @@ import org.springframework.security.access.expression.SecurityExpressionHandler;
|
|||||||
import org.springframework.security.access.expression.SecurityExpressionOperations;
|
import org.springframework.security.access.expression.SecurityExpressionOperations;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.FilterInvocation;
|
import org.springframework.security.web.FilterInvocation;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @author Eddú Meléndez
|
* @author Eddú Meléndez
|
||||||
|
* @author Steve Riesenberg
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class DefaultWebSecurityExpressionHandler extends AbstractSecurityExpressionHandler<FilterInvocation>
|
public class DefaultWebSecurityExpressionHandler extends AbstractSecurityExpressionHandler<FilterInvocation>
|
||||||
implements SecurityExpressionHandler<FilterInvocation> {
|
implements SecurityExpressionHandler<FilterInvocation> {
|
||||||
|
|
||||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
|
||||||
|
|
||||||
private String defaultRolePrefix = "ROLE_";
|
private String defaultRolePrefix = DEFAULT_ROLE_PREFIX;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,
|
protected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,
|
||||||
FilterInvocation fi) {
|
FilterInvocation fi) {
|
||||||
WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, fi);
|
FilterInvocationExpressionRoot root = new FilterInvocationExpressionRoot(() -> authentication, fi);
|
||||||
|
root.setAuthorizationManagerFactory(getAuthorizationManagerFactory());
|
||||||
root.setPermissionEvaluator(getPermissionEvaluator());
|
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||||
root.setTrustResolver(this.trustResolver);
|
if (!DEFAULT_ROLE_PREFIX.equals(this.defaultRolePrefix)) {
|
||||||
root.setRoleHierarchy(getRoleHierarchy());
|
// Ensure SecurityExpressionRoot can strip the custom role prefix
|
||||||
root.setDefaultRolePrefix(this.defaultRolePrefix);
|
root.setDefaultRolePrefix(this.defaultRolePrefix);
|
||||||
|
}
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,10 +58,12 @@ public class DefaultWebSecurityExpressionHandler extends AbstractSecurityExpress
|
|||||||
* {@link AuthenticationTrustResolverImpl}.
|
* {@link AuthenticationTrustResolverImpl}.
|
||||||
* @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be
|
* @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be
|
||||||
* null.
|
* null.
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
||||||
Assert.notNull(trustResolver, "trustResolver cannot be null");
|
getDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);
|
||||||
this.trustResolver = trustResolver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,8 +80,15 @@ public class DefaultWebSecurityExpressionHandler extends AbstractSecurityExpress
|
|||||||
* If null or empty, then no default role prefix is used.
|
* If null or empty, then no default role prefix is used.
|
||||||
* </p>
|
* </p>
|
||||||
* @param defaultRolePrefix the default prefix to add to roles. Default "ROLE_".
|
* @param defaultRolePrefix the default prefix to add to roles. Default "ROLE_".
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
|
||||||
*/
|
*/
|
||||||
public void setDefaultRolePrefix(String defaultRolePrefix) {
|
@Deprecated(since = "7.0")
|
||||||
|
public void setDefaultRolePrefix(@Nullable String defaultRolePrefix) {
|
||||||
|
if (defaultRolePrefix == null) {
|
||||||
|
defaultRolePrefix = "";
|
||||||
|
}
|
||||||
|
getDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);
|
||||||
this.defaultRolePrefix = defaultRolePrefix;
|
this.defaultRolePrefix = defaultRolePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.web.access.expression;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
import org.springframework.security.web.util.matcher.IpAddressMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Riesenberg
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
final class FilterInvocationExpressionRoot extends SecurityExpressionRoot<FilterInvocation> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows direct access to the request object
|
||||||
|
*/
|
||||||
|
public final HttpServletRequest request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance for the given {@link Supplier} of the {@link Authentication}
|
||||||
|
* and {@link HttpServletRequest}.
|
||||||
|
* @param authentication the {@link Supplier} of the {@link Authentication} to use
|
||||||
|
* @param fi the {@link FilterInvocation} to use
|
||||||
|
*/
|
||||||
|
FilterInvocationExpressionRoot(Supplier<Authentication> authentication, FilterInvocation fi) {
|
||||||
|
super(authentication, fi);
|
||||||
|
this.request = fi.getRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a specific IP address or a range using the IP/Netmask (e.g. 192.168.1.0/24 or
|
||||||
|
* 202.24.0.0/14).
|
||||||
|
* @param ipAddress the address or range of addresses from which the request must
|
||||||
|
* come.
|
||||||
|
* @return true if the IP address of the current request is in the required range.
|
||||||
|
*/
|
||||||
|
public boolean hasIpAddress(String ipAddress) {
|
||||||
|
IpAddressMatcher matcher = new IpAddressMatcher(ipAddress);
|
||||||
|
return matcher.matches(this.request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -24,22 +24,29 @@ import org.jspecify.annotations.Nullable;
|
|||||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.FilterInvocation;
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||||
import org.springframework.security.web.util.matcher.IpAddressMatcher;
|
import org.springframework.security.web.util.matcher.IpAddressMatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @author Evgeniy Cheban
|
* @author Evgeniy Cheban
|
||||||
|
* @author Steve Riesenberg
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class WebSecurityExpressionRoot extends SecurityExpressionRoot {
|
public class WebSecurityExpressionRoot extends SecurityExpressionRoot<RequestAuthorizationContext> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows direct access to the request object
|
* Allows direct access to the request object
|
||||||
*/
|
*/
|
||||||
public final HttpServletRequest request;
|
public final HttpServletRequest request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #WebSecurityExpressionRoot(Supplier, RequestAuthorizationContext)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public WebSecurityExpressionRoot(@Nullable Authentication a, FilterInvocation fi) {
|
public WebSecurityExpressionRoot(@Nullable Authentication a, FilterInvocation fi) {
|
||||||
this(() -> a, fi.getRequest());
|
this(() -> a, new RequestAuthorizationContext(fi.getRequest()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,14 +55,30 @@ public class WebSecurityExpressionRoot extends SecurityExpressionRoot {
|
|||||||
* @param authentication the {@link Supplier} of the {@link Authentication} to use
|
* @param authentication the {@link Supplier} of the {@link Authentication} to use
|
||||||
* @param request the {@link HttpServletRequest} to use
|
* @param request the {@link HttpServletRequest} to use
|
||||||
* @since 5.8
|
* @since 5.8
|
||||||
|
* @deprecated Use
|
||||||
|
* {@link #WebSecurityExpressionRoot(Supplier, RequestAuthorizationContext)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
@SuppressWarnings("NullAway") // https://github.com/uber/NullAway/issues/1246
|
@SuppressWarnings("NullAway") // https://github.com/uber/NullAway/issues/1246
|
||||||
public WebSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication,
|
public WebSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
HttpServletRequest request) {
|
HttpServletRequest request) {
|
||||||
super(authentication);
|
super(authentication, new RequestAuthorizationContext(request));
|
||||||
this.request = request;
|
this.request = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance for the given {@link Supplier} of the {@link Authentication}
|
||||||
|
* and {@link HttpServletRequest}.
|
||||||
|
* @param authentication the {@link Supplier} of the {@link Authentication} to use
|
||||||
|
* @param context the {@link RequestAuthorizationContext} to use
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public WebSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
|
RequestAuthorizationContext context) {
|
||||||
|
super(authentication, context);
|
||||||
|
this.request = context.getRequest();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a specific IP address or a range using the IP/Netmask (e.g. 192.168.1.0/24 or
|
* Takes a specific IP address or a range using the IP/Netmask (e.g. 192.168.1.0/24 or
|
||||||
* 202.24.0.0/14).
|
* 202.24.0.0/14).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user