PathPatternRequestMatcherBuilderFactoryBean.java
/*
* 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.config.web;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* Use this factory bean to configure the {@link PathPatternRequestMatcher.Builder} bean
* used to create request matchers in {@link AuthorizeHttpRequestsConfigurer} and other
* parts of the DSL.
*
* @author Josh Cummings
* @since 6.5
*/
public final class PathPatternRequestMatcherBuilderFactoryBean implements
FactoryBean<PathPatternRequestMatcher.Builder>, ApplicationContextAware, BeanNameAware, BeanFactoryAware {
static final String MVC_PATTERN_PARSER_BEAN_NAME = "mvcPatternParser";
private final PathPatternParser parser;
private String basePath;
private ApplicationContext context;
private String beanName;
private ConfigurableListableBeanFactory beanFactory;
/**
* Construct this factory bean using the default {@link PathPatternParser}
*
* <p>
* If you are using Spring MVC, it will use the Spring MVC instance.
*/
public PathPatternRequestMatcherBuilderFactoryBean() {
this(null);
}
/**
* Construct this factory bean using this {@link PathPatternParser}.
*
* <p>
* If you are using Spring MVC, it is likely incorrect to call this constructor.
* Please call the default constructor instead.
* @param parser the {@link PathPatternParser} to use
*/
public PathPatternRequestMatcherBuilderFactoryBean(PathPatternParser parser) {
this.parser = parser;
}
@Override
public PathPatternRequestMatcher.Builder getObject() throws Exception {
if (!this.context.containsBean(MVC_PATTERN_PARSER_BEAN_NAME)) {
PathPatternParser parser = (this.parser != null) ? this.parser : PathPatternParser.defaultInstance;
return withPathPatternParser(parser);
}
PathPatternParser mvc = this.context.getBean(MVC_PATTERN_PARSER_BEAN_NAME, PathPatternParser.class);
PathPatternParser parser = (this.parser != null) ? this.parser : mvc;
if (mvc.equals(parser)) {
return withPathPatternParser(parser);
}
throw new IllegalArgumentException("Spring Security and Spring MVC must use the same path pattern parser. "
+ "To have Spring Security use Spring MVC's [" + describe(mvc, MVC_PATTERN_PARSER_BEAN_NAME)
+ "] simply publish this bean [" + describe(this, this.beanName) + "] using its default constructor");
}
private PathPatternRequestMatcher.Builder withPathPatternParser(PathPatternParser parser) {
if (this.basePath == null) {
return PathPatternRequestMatcher.withPathPatternParser(parser);
}
else {
return PathPatternRequestMatcher.withPathPatternParser(parser).basePath(this.basePath);
}
}
@Override
public Class<?> getObjectType() {
return PathPatternRequestMatcher.Builder.class;
}
/**
* Use this as the base path for patterns built by the resulting
* {@link PathPatternRequestMatcher.Builder} instance
* @param basePath the base path to use
* @since 7.0
* @see PathPatternRequestMatcher.Builder#basePath(String)
*/
public void setBasePath(String basePath) {
this.basePath = basePath;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
@Override
public void setBeanName(@NonNull String name) {
this.beanName = name;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory listable) {
this.beanFactory = listable;
}
}
private String describe(Object bean, String name) {
String text = bean.getClass().getSimpleName();
if (name == null) {
return text;
}
text += "defined as '" + name + "'";
if (this.beanFactory == null) {
return text;
}
BeanDefinition bd = this.beanFactory.getBeanDefinition(name);
String description = bd.getResourceDescription();
if (description == null) {
return text;
}
text += " in [" + description + "]";
return text;
}
}