Use AuthorizationManager in <http>
Closes gh-11305
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.config.http;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.beans.factory.xml.XmlReaderContext;
|
||||
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.config.Elements;
|
||||
import org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler;
|
||||
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
|
||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
class AuthorizationFilterParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String ATT_USE_EXPRESSIONS = "use-expressions";
|
||||
|
||||
private static final String ATT_HTTP_METHOD = "method";
|
||||
|
||||
private static final String ATT_PATTERN = "pattern";
|
||||
|
||||
private static final String ATT_ACCESS = "access";
|
||||
|
||||
private static final String ATT_SERVLET_PATH = "servlet-path";
|
||||
|
||||
private String authorizationManagerRef;
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
if (!isUseExpressions(element)) {
|
||||
parserContext.getReaderContext().error("AuthorizationManager must be used with `use-expressions=\"true\"",
|
||||
element);
|
||||
return null;
|
||||
}
|
||||
this.authorizationManagerRef = createAuthorizationManager(element, parserContext);
|
||||
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(AuthorizationFilter.class);
|
||||
filterBuilder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
|
||||
BeanDefinition filter = filterBuilder.addConstructorArgReference(this.authorizationManagerRef)
|
||||
.getBeanDefinition();
|
||||
String id = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);
|
||||
if (StringUtils.hasText(id)) {
|
||||
parserContext.registerComponent(new BeanComponentDefinition(filter, id));
|
||||
parserContext.getRegistry().registerBeanDefinition(id, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
String getAuthorizationManagerRef() {
|
||||
return this.authorizationManagerRef;
|
||||
}
|
||||
|
||||
private String createAuthorizationManager(Element element, ParserContext parserContext) {
|
||||
XmlReaderContext context = parserContext.getReaderContext();
|
||||
String authorizationManagerRef = element.getAttribute("authorization-manager-ref");
|
||||
if (StringUtils.hasText(authorizationManagerRef)) {
|
||||
return authorizationManagerRef;
|
||||
}
|
||||
Element expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER);
|
||||
String expressionHandlerRef = (expressionHandlerElt != null) ? expressionHandlerElt.getAttribute("ref") : null;
|
||||
if (expressionHandlerRef == null) {
|
||||
expressionHandlerRef = registerDefaultExpressionHandler(parserContext);
|
||||
}
|
||||
MatcherType matcherType = MatcherType.fromElement(element);
|
||||
ManagedMap<BeanMetadataElement, BeanDefinition> matcherToExpression = new ManagedMap<>();
|
||||
List<Element> interceptMessages = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
|
||||
for (Element interceptMessage : interceptMessages) {
|
||||
String accessExpression = interceptMessage.getAttribute(ATT_ACCESS);
|
||||
BeanDefinitionBuilder authorizationManager = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(WebExpressionAuthorizationManager.class);
|
||||
authorizationManager.addPropertyReference("expressionHandler", expressionHandlerRef);
|
||||
authorizationManager.addConstructorArgValue(accessExpression);
|
||||
BeanMetadataElement matcher = createMatcher(matcherType, interceptMessage, parserContext);
|
||||
matcherToExpression.put(matcher, authorizationManager.getBeanDefinition());
|
||||
}
|
||||
BeanDefinitionBuilder mds = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherDelegatingAuthorizationManagerFactory.class);
|
||||
mds.setFactoryMethod("createRequestMatcherDelegatingAuthorizationManager");
|
||||
mds.addConstructorArgValue(matcherToExpression);
|
||||
return context.registerWithGeneratedName(mds.getBeanDefinition());
|
||||
}
|
||||
|
||||
private BeanMetadataElement createMatcher(MatcherType matcherType, Element urlElt, ParserContext parserContext) {
|
||||
String path = urlElt.getAttribute(ATT_PATTERN);
|
||||
String matcherRef = urlElt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REQUEST_MATCHER_REF);
|
||||
boolean hasMatcherRef = StringUtils.hasText(matcherRef);
|
||||
if (!hasMatcherRef && !StringUtils.hasText(path)) {
|
||||
parserContext.getReaderContext().error("path attribute cannot be empty or null", urlElt);
|
||||
}
|
||||
String method = urlElt.getAttribute(ATT_HTTP_METHOD);
|
||||
if (!StringUtils.hasText(method)) {
|
||||
method = null;
|
||||
}
|
||||
String servletPath = urlElt.getAttribute(ATT_SERVLET_PATH);
|
||||
if (!StringUtils.hasText(servletPath)) {
|
||||
servletPath = null;
|
||||
}
|
||||
else if (!MatcherType.mvc.equals(matcherType)) {
|
||||
parserContext.getReaderContext().error(
|
||||
ATT_SERVLET_PATH + " is not applicable for request-matcher: '" + matcherType.name() + "'", urlElt);
|
||||
}
|
||||
return hasMatcherRef ? new RuntimeBeanReference(matcherRef)
|
||||
: matcherType.createMatcher(parserContext, path, method, servletPath);
|
||||
}
|
||||
|
||||
String registerDefaultExpressionHandler(ParserContext pc) {
|
||||
BeanDefinition expressionHandler = GrantedAuthorityDefaultsParserUtils.registerWithDefaultRolePrefix(pc,
|
||||
DefaultWebSecurityExpressionHandlerBeanFactory.class);
|
||||
String expressionHandlerRef = pc.getReaderContext().generateBeanName(expressionHandler);
|
||||
pc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));
|
||||
return expressionHandlerRef;
|
||||
}
|
||||
|
||||
boolean isUseExpressions(Element elt) {
|
||||
String useExpressions = elt.getAttribute(ATT_USE_EXPRESSIONS);
|
||||
return !StringUtils.hasText(useExpressions) || "true".equals(useExpressions);
|
||||
}
|
||||
|
||||
private static class RequestMatcherDelegatingAuthorizationManagerFactory {
|
||||
|
||||
private static AuthorizationManager<HttpServletRequest> createRequestMatcherDelegatingAuthorizationManager(
|
||||
Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> beans) {
|
||||
RequestMatcherDelegatingAuthorizationManager.Builder builder = RequestMatcherDelegatingAuthorizationManager
|
||||
.builder();
|
||||
for (Map.Entry<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> entry : beans
|
||||
.entrySet()) {
|
||||
builder.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return builder.add(AnyRequestMatcher.INSTANCE, AuthenticatedAuthorizationManager.authenticated()).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class DefaultWebSecurityExpressionHandlerBeanFactory
|
||||
extends GrantedAuthorityDefaultsParserUtils.AbstractGrantedAuthorityDefaultsBeanFactory {
|
||||
|
||||
private DefaultHttpSecurityExpressionHandler handler = new DefaultHttpSecurityExpressionHandler();
|
||||
|
||||
@Override
|
||||
public DefaultHttpSecurityExpressionHandler getBean() {
|
||||
this.handler.setDefaultRolePrefix(this.rolePrefix);
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import org.springframework.security.access.vote.RoleVoter;
|
||||
import org.springframework.security.config.Elements;
|
||||
import org.springframework.security.config.http.GrantedAuthorityDefaultsParserUtils.AbstractGrantedAuthorityDefaultsBeanFactory;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
@@ -112,6 +113,10 @@ class HttpConfigurationBuilder {
|
||||
|
||||
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
|
||||
|
||||
private static final String ATT_USE_AUTHORIZATION_MGR = "use-authorization-manager";
|
||||
|
||||
private static final String ATT_AUTHORIZATION_MGR = "authorization-manager-ref";
|
||||
|
||||
private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
|
||||
|
||||
private static final String ATT_ONCE_PER_REQUEST = "once-per-request";
|
||||
@@ -219,7 +224,7 @@ class HttpConfigurationBuilder {
|
||||
createServletApiFilter(authenticationManager);
|
||||
createJaasApiFilter();
|
||||
createChannelProcessingFilter();
|
||||
createFilterSecurityInterceptor(authenticationManager);
|
||||
createFilterSecurity(authenticationManager);
|
||||
createAddHeadersFilter();
|
||||
createCorsFilter();
|
||||
createWellKnownChangePasswordRedirectFilter();
|
||||
@@ -674,6 +679,35 @@ class HttpConfigurationBuilder {
|
||||
this.requestCacheAwareFilter.getConstructorArgumentValues().addGenericArgumentValue(this.requestCache);
|
||||
}
|
||||
|
||||
private void createFilterSecurity(BeanReference authManager) {
|
||||
boolean useAuthorizationManager = Boolean.parseBoolean(this.httpElt.getAttribute(ATT_USE_AUTHORIZATION_MGR));
|
||||
if (useAuthorizationManager) {
|
||||
createAuthorizationFilter();
|
||||
return;
|
||||
}
|
||||
if (StringUtils.hasText(this.httpElt.getAttribute(ATT_AUTHORIZATION_MGR))) {
|
||||
createAuthorizationFilter();
|
||||
return;
|
||||
}
|
||||
createFilterSecurityInterceptor(authManager);
|
||||
}
|
||||
|
||||
private void createAuthorizationFilter() {
|
||||
AuthorizationFilterParser authorizationFilterParser = new AuthorizationFilterParser();
|
||||
BeanDefinition fsiBean = authorizationFilterParser.parse(this.httpElt, this.pc);
|
||||
String fsiId = this.pc.getReaderContext().generateBeanName(fsiBean);
|
||||
this.pc.registerBeanComponent(new BeanComponentDefinition(fsiBean, fsiId));
|
||||
// Create and register a AuthorizationManagerWebInvocationPrivilegeEvaluator for
|
||||
// use with
|
||||
// taglibs etc.
|
||||
BeanDefinition wipe = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AuthorizationManagerWebInvocationPrivilegeEvaluator.class)
|
||||
.addConstructorArgReference(authorizationFilterParser.getAuthorizationManagerRef()).getBeanDefinition();
|
||||
this.pc.registerBeanComponent(
|
||||
new BeanComponentDefinition(wipe, this.pc.getReaderContext().generateBeanName(wipe)));
|
||||
this.fsi = new RuntimeBeanReference(fsiId);
|
||||
}
|
||||
|
||||
private void createFilterSecurityInterceptor(BeanReference authManager) {
|
||||
boolean useExpressions = FilterInvocationSecurityMetadataSourceParser.isUseExpressions(this.httpElt);
|
||||
RootBeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser
|
||||
|
||||
@@ -350,6 +350,12 @@ http.attlist &=
|
||||
http.attlist &=
|
||||
## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to "false".
|
||||
attribute jaas-api-provision {xsd:boolean}?
|
||||
http.attlist &=
|
||||
## Use AuthorizationManager API instead of SecurityMetadataSource
|
||||
attribute use-authorization-manager {xsd:boolean}?
|
||||
http.attlist &=
|
||||
## Use this AuthorizationManager instead of deriving one from <intercept-url> elements
|
||||
attribute authorization-manager-ref {xsd:token}?
|
||||
http.attlist &=
|
||||
## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.
|
||||
attribute access-decision-manager-ref {xsd:token}?
|
||||
|
||||
@@ -1287,6 +1287,18 @@
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="use-authorization-manager" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authorization-manager-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Use this AuthorizationManager instead of deriving one from <intercept-url> elements
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="access-decision-manager-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which
|
||||
|
||||
@@ -350,6 +350,12 @@ http.attlist &=
|
||||
http.attlist &=
|
||||
## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to "false".
|
||||
attribute jaas-api-provision {xsd:boolean}?
|
||||
http.attlist &=
|
||||
## Use AuthorizationManager API instead of SecurityMetadataSource
|
||||
attribute use-authorization-manager {xsd:boolean}?
|
||||
http.attlist &=
|
||||
## Use this AuthorizationManager instead of deriving one from <intercept-url> elements
|
||||
attribute authorization-manager-ref {xsd:token}?
|
||||
http.attlist &=
|
||||
## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.
|
||||
attribute access-decision-manager-ref {xsd:token}?
|
||||
|
||||
@@ -1265,6 +1265,18 @@
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="use-authorization-manager" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authorization-manager-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Use this AuthorizationManager instead of deriving one from <intercept-url> elements
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="access-decision-manager-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which
|
||||
|
||||
Reference in New Issue
Block a user