Remove Direct Runtime Dependency on Access API
Issue gh-17847
This commit is contained in:
parent
10935632ee
commit
3a1692f3c3
|
@ -23,9 +23,7 @@ import java.util.Map;
|
||||||
import jakarta.servlet.Filter;
|
import jakarta.servlet.Filter;
|
||||||
|
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
|
||||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
|
||||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.AuthenticationFilter;
|
import org.springframework.security.web.authentication.AuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
@ -78,7 +76,7 @@ final class FilterOrderRegistration {
|
||||||
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
|
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
|
||||||
put(DisableEncodeUrlFilter.class, order.next());
|
put(DisableEncodeUrlFilter.class, order.next());
|
||||||
put(ForceEagerSessionCreationFilter.class, order.next());
|
put(ForceEagerSessionCreationFilter.class, order.next());
|
||||||
put(ChannelProcessingFilter.class, order.next());
|
this.filterToOrder.put("org.springframework.security.web.access.channel.ChannelProcessingFilter", order.next());
|
||||||
put(HttpsRedirectFilter.class, order.next());
|
put(HttpsRedirectFilter.class, order.next());
|
||||||
order.next(); // gh-8105
|
order.next(); // gh-8105
|
||||||
put(WebAsyncManagerIntegrationFilter.class, order.next());
|
put(WebAsyncManagerIntegrationFilter.class, order.next());
|
||||||
|
@ -126,7 +124,8 @@ final class FilterOrderRegistration {
|
||||||
order.next());
|
order.next());
|
||||||
put(SessionManagementFilter.class, order.next());
|
put(SessionManagementFilter.class, order.next());
|
||||||
put(ExceptionTranslationFilter.class, order.next());
|
put(ExceptionTranslationFilter.class, order.next());
|
||||||
put(FilterSecurityInterceptor.class, order.next());
|
this.filterToOrder.put("org.springframework.security.web.access.intercept.FilterSecurityInterceptor",
|
||||||
|
order.next());
|
||||||
put(AuthorizationFilter.class, order.next());
|
put(AuthorizationFilter.class, order.next());
|
||||||
put(SwitchUserFilter.class, order.next());
|
put(SwitchUserFilter.class, order.next());
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.security.config.annotation.web.builders;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
import jakarta.servlet.Filter;
|
import jakarta.servlet.Filter;
|
||||||
|
@ -25,6 +26,8 @@ import jakarta.servlet.ServletContext;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
@ -32,8 +35,11 @@ import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.security.access.PermissionEvaluator;
|
import org.springframework.security.access.PermissionEvaluator;
|
||||||
|
import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
|
||||||
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.hierarchicalroles.RoleHierarchy;
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
@ -46,6 +52,7 @@ import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||||
import org.springframework.security.web.FilterChainProxy;
|
import org.springframework.security.web.FilterChainProxy;
|
||||||
|
@ -58,7 +65,7 @@ import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEval
|
||||||
import org.springframework.security.web.access.PathPatternRequestTransformer;
|
import org.springframework.security.web.access.PathPatternRequestTransformer;
|
||||||
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
|
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
import org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler;
|
||||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||||
|
@ -74,6 +81,7 @@ import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||||
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.ClassUtils;
|
||||||
import org.springframework.web.context.ServletContextAware;
|
import org.springframework.web.context.ServletContextAware;
|
||||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||||
|
|
||||||
|
@ -99,6 +107,9 @@ import org.springframework.web.filter.DelegatingFilterProxy;
|
||||||
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
|
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
|
||||||
implements SecurityBuilder<Filter>, ApplicationContextAware, ServletContextAware {
|
implements SecurityBuilder<Filter>, ApplicationContextAware, ServletContextAware {
|
||||||
|
|
||||||
|
private static final boolean USING_ACCESS = ClassUtils
|
||||||
|
.isPresent("org.springframework.security.access.SecurityConfig", null);
|
||||||
|
|
||||||
private final Log logger = LogFactory.getLog(getClass());
|
private final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
|
private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
|
||||||
|
@ -122,9 +133,10 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
|
|
||||||
private HttpServletRequestTransformer privilegeEvaluatorRequestTransformer;
|
private HttpServletRequestTransformer privilegeEvaluatorRequestTransformer;
|
||||||
|
|
||||||
private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
|
private DefaultHttpSecurityExpressionHandler defaultExpressionHandler = new DefaultHttpSecurityExpressionHandler();
|
||||||
|
|
||||||
private SecurityExpressionHandler<FilterInvocation> expressionHandler = this.defaultWebSecurityExpressionHandler;
|
private SecurityExpressionHandler<FilterInvocation> expressionHandler = new SecurityExpressionHandlerAdapter(
|
||||||
|
this.defaultExpressionHandler);
|
||||||
|
|
||||||
private Runnable postBuildAction = () -> {
|
private Runnable postBuildAction = () -> {
|
||||||
};
|
};
|
||||||
|
@ -240,7 +252,7 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the {@link SecurityExpressionHandler} to be used. If this is not specified,
|
* Set the {@link SecurityExpressionHandler} to be used. If this is not specified,
|
||||||
* then a {@link DefaultWebSecurityExpressionHandler} will be used.
|
* then a {@link DefaultHttpSecurityExpressionHandler} will be used.
|
||||||
* @param expressionHandler the {@link SecurityExpressionHandler} to use
|
* @param expressionHandler the {@link SecurityExpressionHandler} to use
|
||||||
* @return the {@link WebSecurity} for further customizations
|
* @return the {@link WebSecurity} for further customizations
|
||||||
*/
|
*/
|
||||||
|
@ -361,19 +373,9 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
RequestMatcherDelegatingAuthorizationManager.Builder builder) {
|
RequestMatcherDelegatingAuthorizationManager.Builder builder) {
|
||||||
boolean mappings = false;
|
boolean mappings = false;
|
||||||
for (Filter filter : securityFilterChain.getFilters()) {
|
for (Filter filter : securityFilterChain.getFilters()) {
|
||||||
if (filter instanceof FilterSecurityInterceptor securityInterceptor) {
|
if (USING_ACCESS) {
|
||||||
DefaultWebInvocationPrivilegeEvaluator privilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
|
mappings = AccessComponents.addAuthorizationManager(filter, this.servletContext, builder,
|
||||||
securityInterceptor);
|
securityFilterChain);
|
||||||
privilegeEvaluator.setServletContext(this.servletContext);
|
|
||||||
AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, context) -> {
|
|
||||||
HttpServletRequest request = context.getRequest();
|
|
||||||
boolean result = privilegeEvaluator.isAllowed(request.getContextPath(), request.getRequestURI(),
|
|
||||||
request.getMethod(), authentication.get());
|
|
||||||
return new AuthorizationDecision(result);
|
|
||||||
};
|
|
||||||
builder.add(securityFilterChain::matches, authorizationManager);
|
|
||||||
mappings = true;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (filter instanceof AuthorizationFilter authorization) {
|
if (filter instanceof AuthorizationFilter authorization) {
|
||||||
AuthorizationManager<HttpServletRequest> authorizationManager = authorization.getAuthorizationManager();
|
AuthorizationManager<HttpServletRequest> authorizationManager = authorization.getAuthorizationManager();
|
||||||
|
@ -388,15 +390,14 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
this.defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
|
this.defaultExpressionHandler.setApplicationContext(applicationContext);
|
||||||
try {
|
try {
|
||||||
this.defaultWebSecurityExpressionHandler.setRoleHierarchy(applicationContext.getBean(RoleHierarchy.class));
|
this.defaultExpressionHandler.setRoleHierarchy(applicationContext.getBean(RoleHierarchy.class));
|
||||||
}
|
}
|
||||||
catch (NoSuchBeanDefinitionException ex) {
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.defaultWebSecurityExpressionHandler
|
this.defaultExpressionHandler.setPermissionEvaluator(applicationContext.getBean(PermissionEvaluator.class));
|
||||||
.setPermissionEvaluator(applicationContext.getBean(PermissionEvaluator.class));
|
|
||||||
}
|
}
|
||||||
catch (NoSuchBeanDefinitionException ex) {
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
}
|
}
|
||||||
|
@ -463,4 +464,76 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NullMarked
|
||||||
|
private static final class SecurityExpressionHandlerAdapter
|
||||||
|
extends AbstractSecurityExpressionHandler<FilterInvocation> {
|
||||||
|
|
||||||
|
private final AbstractSecurityExpressionHandler<RequestAuthorizationContext> delegate;
|
||||||
|
|
||||||
|
private SecurityExpressionHandlerAdapter(
|
||||||
|
AbstractSecurityExpressionHandler<RequestAuthorizationContext> delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
|
FilterInvocation invocation) {
|
||||||
|
RequestAuthorizationContext context = new RequestAuthorizationContext(invocation.getRequest());
|
||||||
|
return this.delegate.createEvaluationContext(authentication, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,
|
||||||
|
FilterInvocation invocation) {
|
||||||
|
RequestAuthorizationContext context = new RequestAuthorizationContext(invocation.getRequest());
|
||||||
|
Object operations = this.delegate.createEvaluationContext(authentication, context)
|
||||||
|
.getRootObject()
|
||||||
|
.getValue();
|
||||||
|
Assert.isInstanceOf(SecurityExpressionOperations.class, operations,
|
||||||
|
"createEvaluationContext must have a SecurityExpressionOperations instance as its root");
|
||||||
|
return (SecurityExpressionOperations) operations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext context) {
|
||||||
|
this.delegate.setApplicationContext(context);
|
||||||
|
super.setApplicationContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
|
||||||
|
this.delegate.setPermissionEvaluator(permissionEvaluator);
|
||||||
|
super.setPermissionEvaluator(permissionEvaluator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {
|
||||||
|
this.delegate.setRoleHierarchy(roleHierarchy);
|
||||||
|
super.setRoleHierarchy(roleHierarchy);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class AccessComponents {
|
||||||
|
|
||||||
|
private static boolean addAuthorizationManager(Filter filter, ServletContext servletContext,
|
||||||
|
RequestMatcherDelegatingAuthorizationManager.Builder builder, SecurityFilterChain securityFilterChain) {
|
||||||
|
if (filter instanceof FilterSecurityInterceptor securityInterceptor) {
|
||||||
|
DefaultWebInvocationPrivilegeEvaluator privilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
|
||||||
|
securityInterceptor);
|
||||||
|
privilegeEvaluator.setServletContext(servletContext);
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, context) -> {
|
||||||
|
HttpServletRequest request = context.getRequest();
|
||||||
|
boolean result = privilegeEvaluator.isAllowed(request.getContextPath(), request.getRequestURI(),
|
||||||
|
request.getMethod(), authentication.get());
|
||||||
|
return new AuthorizationDecision(result);
|
||||||
|
};
|
||||||
|
builder.add(securityFilterChain::matches, authorizationManager);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.springframework.security.web.UnreachableFilterChainException;
|
||||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A filter chain validator for filter chains built by {@link WebSecurity}
|
* A filter chain validator for filter chains built by {@link WebSecurity}
|
||||||
|
@ -39,6 +40,9 @@ import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||||
*/
|
*/
|
||||||
final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterChainValidator {
|
final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterChainValidator {
|
||||||
|
|
||||||
|
private static final boolean USING_ACCESS = ClassUtils
|
||||||
|
.isPresent("org.springframework.security.access.SecurityConfig", null);
|
||||||
|
|
||||||
private final Log logger = LogFactory.getLog(getClass());
|
private final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -93,7 +97,7 @@ final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterCh
|
||||||
if (filter instanceof AuthorizationFilter) {
|
if (filter instanceof AuthorizationFilter) {
|
||||||
authorizationFilter = filter;
|
authorizationFilter = filter;
|
||||||
}
|
}
|
||||||
if (filter instanceof FilterSecurityInterceptor) {
|
if (USING_ACCESS && AccessComponents.isFilterSecurityInterceptor(filter)) {
|
||||||
filterSecurityInterceptor = filter;
|
filterSecurityInterceptor = filter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,4 +114,12 @@ final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterCh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class AccessComponents {
|
||||||
|
|
||||||
|
private static boolean isFilterSecurityInterceptor(Filter filter) {
|
||||||
|
return filter instanceof FilterSecurityInterceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,9 +56,13 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||||
import org.springframework.security.web.session.SessionManagementFilter;
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
public class DefaultFilterChainValidator implements FilterChainProxy.FilterChainValidator {
|
public class DefaultFilterChainValidator implements FilterChainProxy.FilterChainValidator {
|
||||||
|
|
||||||
|
private static final boolean USING_ACCESS = ClassUtils
|
||||||
|
.isPresent("org.springframework.security.access.SecurityConfig", null);
|
||||||
|
|
||||||
private static final Authentication TEST = new TestingAuthenticationToken("", "", Collections.emptyList());
|
private static final Authentication TEST = new TestingAuthenticationToken("", "", Collections.emptyList());
|
||||||
|
|
||||||
private final Log logger = LogFactory.getLog(getClass());
|
private final Log logger = LogFactory.getLog(getClass());
|
||||||
|
@ -120,7 +124,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
|
||||||
if (filter instanceof AuthorizationFilter) {
|
if (filter instanceof AuthorizationFilter) {
|
||||||
authorizationFilter = filter;
|
authorizationFilter = filter;
|
||||||
}
|
}
|
||||||
if (filter instanceof FilterSecurityInterceptor) {
|
if (USING_ACCESS && AccessComponents.isFilterSecurityInterceptor(filter)) {
|
||||||
filterSecurityInterceptor = filter;
|
filterSecurityInterceptor = filter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +142,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked" })
|
@SuppressWarnings({ "unchecked" })
|
||||||
private <F extends Filter> F getFilter(Class<F> type, List<Filter> filters) {
|
private static <F extends Filter> F getFilter(Class<F> type, List<Filter> filters) {
|
||||||
for (Filter f : filters) {
|
for (Filter f : filters) {
|
||||||
if (type.isAssignableFrom(f.getClass())) {
|
if (type.isAssignableFrom(f.getClass())) {
|
||||||
return (F) f;
|
return (F) f;
|
||||||
|
@ -158,7 +162,9 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
|
||||||
checkForDuplicates(SecurityContextHolderAwareRequestFilter.class, filters);
|
checkForDuplicates(SecurityContextHolderAwareRequestFilter.class, filters);
|
||||||
checkForDuplicates(JaasApiIntegrationFilter.class, filters);
|
checkForDuplicates(JaasApiIntegrationFilter.class, filters);
|
||||||
checkForDuplicates(ExceptionTranslationFilter.class, filters);
|
checkForDuplicates(ExceptionTranslationFilter.class, filters);
|
||||||
checkForDuplicates(FilterSecurityInterceptor.class, filters);
|
if (USING_ACCESS) {
|
||||||
|
checkForDuplicates(AccessComponents.getFilterSecurityInterceptorClass(), filters);
|
||||||
|
}
|
||||||
checkForDuplicates(AuthorizationFilter.class, filters);
|
checkForDuplicates(AuthorizationFilter.class, filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,19 +249,11 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkLoginPageIsPublic(List<Filter> filters, HttpServletRequest loginRequest) {
|
private boolean checkLoginPageIsPublic(List<Filter> filters, HttpServletRequest loginRequest) {
|
||||||
FilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);
|
if (USING_ACCESS) {
|
||||||
if (authorizationInterceptor != null) {
|
Boolean isPublic = AccessComponents.checkLoginPageIsPublic(filters, loginRequest);
|
||||||
FilterInvocationSecurityMetadataSource fids = authorizationInterceptor.getSecurityMetadataSource();
|
if (isPublic != null) {
|
||||||
Collection<ConfigAttribute> attributes = fids.getAttributes(loginRequest);
|
return isPublic;
|
||||||
if (attributes == null) {
|
|
||||||
this.logger.debug("No access attributes defined for login page URL");
|
|
||||||
if (authorizationInterceptor.isRejectPublicInvocations()) {
|
|
||||||
this.logger.warn("FilterSecurityInterceptor is configured to reject public invocations."
|
|
||||||
+ " Your login page may not be accessible.");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
AuthorizationFilter authorizationFilter = getFilter(AuthorizationFilter.class, filters);
|
AuthorizationFilter authorizationFilter = getFilter(AuthorizationFilter.class, filters);
|
||||||
if (authorizationFilter != null) {
|
if (authorizationFilter != null) {
|
||||||
|
@ -274,19 +272,11 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
|
||||||
|
|
||||||
private Supplier<Boolean> deriveAnonymousCheck(List<Filter> filters, HttpServletRequest loginRequest,
|
private Supplier<Boolean> deriveAnonymousCheck(List<Filter> filters, HttpServletRequest loginRequest,
|
||||||
AnonymousAuthenticationToken token) {
|
AnonymousAuthenticationToken token) {
|
||||||
FilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);
|
if (USING_ACCESS) {
|
||||||
if (authorizationInterceptor != null) {
|
Supplier<Boolean> check = AccessComponents.getAnonymousCheck(filters, loginRequest, token);
|
||||||
return () -> {
|
if (check != null) {
|
||||||
FilterInvocationSecurityMetadataSource source = authorizationInterceptor.getSecurityMetadataSource();
|
return check;
|
||||||
Collection<ConfigAttribute> attributes = source.getAttributes(loginRequest);
|
}
|
||||||
try {
|
|
||||||
authorizationInterceptor.getAccessDecisionManager().decide(token, loginRequest, attributes);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (AccessDeniedException ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
AuthorizationFilter authorizationFilter = getFilter(AuthorizationFilter.class, filters);
|
AuthorizationFilter authorizationFilter = getFilter(AuthorizationFilter.class, filters);
|
||||||
if (authorizationFilter != null) {
|
if (authorizationFilter != null) {
|
||||||
|
@ -300,4 +290,55 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
|
||||||
return () -> true;
|
return () -> true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class AccessComponents {
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(DefaultFilterChainValidator.class);
|
||||||
|
|
||||||
|
private static boolean isFilterSecurityInterceptor(Filter filter) {
|
||||||
|
return filter instanceof FilterSecurityInterceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Class<FilterSecurityInterceptor> getFilterSecurityInterceptorClass() {
|
||||||
|
return FilterSecurityInterceptor.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Boolean checkLoginPageIsPublic(List<Filter> filters, HttpServletRequest loginRequest) {
|
||||||
|
FilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);
|
||||||
|
if (authorizationInterceptor == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
FilterInvocationSecurityMetadataSource fids = authorizationInterceptor.getSecurityMetadataSource();
|
||||||
|
Collection<ConfigAttribute> attributes = fids.getAttributes(loginRequest);
|
||||||
|
if (attributes == null) {
|
||||||
|
logger.debug("No access attributes defined for login page URL");
|
||||||
|
if (authorizationInterceptor.isRejectPublicInvocations()) {
|
||||||
|
logger.warn("FilterSecurityInterceptor is configured to reject public invocations."
|
||||||
|
+ " Your login page may not be accessible.");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Supplier<Boolean> getAnonymousCheck(List<Filter> filters, HttpServletRequest loginRequest,
|
||||||
|
AnonymousAuthenticationToken token) {
|
||||||
|
FilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);
|
||||||
|
if (authorizationInterceptor == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return () -> {
|
||||||
|
FilterInvocationSecurityMetadataSource source = authorizationInterceptor.getSecurityMetadataSource();
|
||||||
|
Collection<ConfigAttribute> attributes = source.getAttributes(loginRequest);
|
||||||
|
try {
|
||||||
|
authorizationInterceptor.getAccessDecisionManager().decide(token, loginRequest, attributes);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (AccessDeniedException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,6 @@ import org.springframework.security.web.access.AuthorizationManagerWebInvocation
|
||||||
import org.springframework.security.web.access.PathPatternRequestTransformer;
|
import org.springframework.security.web.access.PathPatternRequestTransformer;
|
||||||
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
|
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
|
||||||
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
|
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
@ -153,7 +152,7 @@ public class WebSecurityConfigurationTests {
|
||||||
public void loadConfigWhenDefaultSecurityExpressionHandlerThenDefaultIsRegistered() {
|
public void loadConfigWhenDefaultSecurityExpressionHandlerThenDefaultIsRegistered() {
|
||||||
this.spring.register(WebSecurityExpressionHandlerDefaultsConfig.class).autowire();
|
this.spring.register(WebSecurityExpressionHandlerDefaultsConfig.class).autowire();
|
||||||
assertThat(this.spring.getContext().getBean(SecurityExpressionHandler.class))
|
assertThat(this.spring.getContext().getBean(SecurityExpressionHandler.class))
|
||||||
.isInstanceOf(DefaultWebSecurityExpressionHandler.class);
|
.isInstanceOf(AbstractSecurityExpressionHandler.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -23,7 +23,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.test.context.showcase.service.HelloMessageService;
|
import org.springframework.security.test.context.showcase.service.HelloMessageService;
|
||||||
import org.springframework.security.test.context.showcase.service.MessageService;
|
import org.springframework.security.test.context.showcase.service.MessageService;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
@ -48,7 +49,8 @@ public class WithMockUserParentTests extends WithMockUserParent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
@EnableMethodSecurity
|
||||||
|
@EnableWebSecurity
|
||||||
@ComponentScan(basePackageClasses = HelloMessageService.class)
|
@ComponentScan(basePackageClasses = HelloMessageService.class)
|
||||||
static class Config {
|
static class Config {
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@ import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.AliasFor;
|
import org.springframework.core.annotation.AliasFor;
|
||||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.test.context.showcase.service.HelloMessageService;
|
import org.springframework.security.test.context.showcase.service.HelloMessageService;
|
||||||
import org.springframework.security.test.context.showcase.service.MessageService;
|
import org.springframework.security.test.context.showcase.service.MessageService;
|
||||||
import org.springframework.security.test.context.support.WithMockUser;
|
import org.springframework.security.test.context.support.WithMockUser;
|
||||||
|
@ -53,8 +54,8 @@ public class WithMockUserTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getMessageUnauthenticated() {
|
public void getMessageUnauthenticated() {
|
||||||
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
|
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.messageService.getMessage())
|
||||||
.isThrownBy(() -> this.messageService.getMessage());
|
.withRootCauseInstanceOf(AuthenticationCredentialsNotFoundException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -104,7 +105,8 @@ public class WithMockUserTests {
|
||||||
assertThat(message).contains("admin").contains("ADMIN").contains("ROLE_ADMIN");
|
assertThat(message).contains("admin").contains("ADMIN").contains("ROLE_ADMIN");
|
||||||
}
|
}
|
||||||
|
|
||||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
@EnableMethodSecurity
|
||||||
|
@EnableWebSecurity
|
||||||
@ComponentScan(basePackageClasses = HelloMessageService.class)
|
@ComponentScan(basePackageClasses = HelloMessageService.class)
|
||||||
static class Config {
|
static class Config {
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
@ -51,8 +52,8 @@ public class WithUserDetailsTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getMessageUnauthenticated() {
|
public void getMessageUnauthenticated() {
|
||||||
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
|
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.messageService.getMessage())
|
||||||
.isThrownBy(() -> this.messageService.getMessage());
|
.withRootCauseInstanceOf(AuthenticationCredentialsNotFoundException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -84,7 +85,8 @@ public class WithUserDetailsTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
@EnableMethodSecurity
|
||||||
|
@EnableWebSecurity
|
||||||
@ComponentScan(basePackageClasses = HelloMessageService.class)
|
@ComponentScan(basePackageClasses = HelloMessageService.class)
|
||||||
static class Config {
|
static class Config {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue