spring security ProtectPointcutPostProcessor 源码

  • 2022-08-13
  • 浏览 (417)

spring security ProtectPointcutPostProcessor 代码

文件路径:/config/src/main/java/org/springframework/security/config/method/ProtectPointcutPostProcessor.java

/*
 * 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.method;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;
import org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * Parses AspectJ pointcut expressions, registering methods that match the pointcut with a
 * traditional {@link MapBasedMethodSecurityMetadataSource}.
 *
 * <p>
 * This class provides a convenient way of declaring a list of pointcuts, and then having
 * every method of every bean defined in the Spring application context compared with
 * those pointcuts. Where a match is found, the matching method will be registered with
 * the {@link MapBasedMethodSecurityMetadataSource}.
 * <p>
 * It is very important to understand that only the <b>first</b> pointcut that matches a
 * given method will be taken as authoritative for that method. This is why pointcuts
 * should be provided as a <tt>LinkedHashMap</tt>, because their order is very important.
 * <p>
 * Note also that only beans defined in the Spring application context will be examined by
 * this class.
 * <p>
 * Because this class registers method security metadata with
 * {@link MapBasedMethodSecurityMetadataSource}, normal Spring Security capabilities such
 * as {@link MethodSecurityMetadataSourceAdvisor} can be used. It does not matter the fact
 * the method metadata was originally obtained from an AspectJ pointcut expression
 * evaluation.
 *
 * @author Ben Alex
 * @since 2.0
 */
final class ProtectPointcutPostProcessor implements BeanPostProcessor {

	private static final Log logger = LogFactory.getLog(ProtectPointcutPostProcessor.class);

	private final Map<String, List<ConfigAttribute>> pointcutMap = new LinkedHashMap<>();

	private final MapBasedMethodSecurityMetadataSource mapBasedMethodSecurityMetadataSource;

	private final Set<PointcutExpression> pointCutExpressions = new LinkedHashSet<>();

	private final PointcutParser parser;

	private final Set<String> processedBeans = new HashSet<>();

	ProtectPointcutPostProcessor(MapBasedMethodSecurityMetadataSource mapBasedMethodSecurityMetadataSource) {
		Assert.notNull(mapBasedMethodSecurityMetadataSource,
				"MapBasedMethodSecurityMetadataSource to populate is required");
		this.mapBasedMethodSecurityMetadataSource = mapBasedMethodSecurityMetadataSource;
		// Set up AspectJ pointcut expression parser
		Set<PointcutPrimitive> supportedPrimitives = new HashSet<>(3);
		supportedPrimitives.add(PointcutPrimitive.EXECUTION);
		supportedPrimitives.add(PointcutPrimitive.ARGS);
		supportedPrimitives.add(PointcutPrimitive.REFERENCE);
		// supportedPrimitives.add(PointcutPrimitive.THIS);
		// supportedPrimitives.add(PointcutPrimitive.TARGET);
		// supportedPrimitives.add(PointcutPrimitive.WITHIN);
		// supportedPrimitives.add(PointcutPrimitive.AT_ANNOTATION);
		// supportedPrimitives.add(PointcutPrimitive.AT_WITHIN);
		// supportedPrimitives.add(PointcutPrimitive.AT_ARGS);
		// supportedPrimitives.add(PointcutPrimitive.AT_TARGET);
		this.parser = PointcutParser
				.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(
						supportedPrimitives);
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (this.processedBeans.contains(beanName)) {
			// We already have the metadata for this bean
			return bean;
		}
		synchronized (this.processedBeans) {
			// check again synchronized this time
			if (this.processedBeans.contains(beanName)) {
				return bean;
			}
			// Obtain methods for the present bean
			Method[] methods = getBeanMethods(bean);
			// Check to see if any of those methods are compatible with our pointcut
			// expressions
			for (Method method : methods) {
				for (PointcutExpression expression : this.pointCutExpressions) {
					// Try for the bean class directly
					if (attemptMatch(bean.getClass(), method, expression, beanName)) {
						// We've found the first expression that matches this method, so
						// move onto the next method now
						break; // the "while" loop, not the "for" loop
					}
				}
			}
			this.processedBeans.add(beanName);
		}
		return bean;
	}

	private Method[] getBeanMethods(Object bean) {
		try {
			return bean.getClass().getMethods();
		}
		catch (Exception ex) {
			throw new IllegalStateException(ex.getMessage());
		}
	}

	private boolean attemptMatch(Class<?> targetClass, Method method, PointcutExpression expression, String beanName) {
		// Determine if the presented AspectJ pointcut expression matches this method
		boolean matches = expression.matchesMethodExecution(method).alwaysMatches();
		// Handle accordingly
		if (matches) {
			List<ConfigAttribute> attr = this.pointcutMap.get(expression.getPointcutExpression());
			if (logger.isDebugEnabled()) {
				logger.debug("AspectJ pointcut expression '" + expression.getPointcutExpression()
						+ "' matches target class '" + targetClass.getName() + "' (bean ID '" + beanName
						+ "') for method '" + method + "'; registering security configuration attribute '" + attr
						+ "'");
			}
			this.mapBasedMethodSecurityMetadataSource.addSecureMethod(targetClass, method, attr);
		}
		return matches;
	}

	public void setPointcutMap(Map<String, List<ConfigAttribute>> map) {
		Assert.notEmpty(map, "configAttributes cannot be empty");
		for (String expression : map.keySet()) {
			List<ConfigAttribute> value = map.get(expression);
			addPointcut(expression, value);
		}
	}

	private void addPointcut(String pointcutExpression, List<ConfigAttribute> definition) {
		Assert.hasText(pointcutExpression, "An AspectJ pointcut expression is required");
		Assert.notNull(definition, "A List of ConfigAttributes is required");
		pointcutExpression = replaceBooleanOperators(pointcutExpression);
		this.pointcutMap.put(pointcutExpression, definition);
		// Parse the presented AspectJ pointcut expression and add it to the cache
		this.pointCutExpressions.add(this.parser.parsePointcutExpression(pointcutExpression));
		if (logger.isDebugEnabled()) {
			logger.debug("AspectJ pointcut expression '" + pointcutExpression
					+ "' registered for security configuration attribute '" + definition + "'");
		}
	}

	/**
	 * @see org.springframework.aop.aspectj.AspectJExpressionPointcut#replaceBooleanOperators
	 */
	private String replaceBooleanOperators(String pcExpr) {
		pcExpr = StringUtils.replace(pcExpr, " and ", " && ");
		pcExpr = StringUtils.replace(pcExpr, " or ", " || ");
		pcExpr = StringUtils.replace(pcExpr, " not ", " ! ");
		return pcExpr;
	}

}

相关信息

spring security 源码目录

相关文章

spring security AspectJMethodMatcher 源码

spring security GlobalMethodSecurityBeanDefinitionParser 源码

spring security InterceptMethodsBeanDefinitionDecorator 源码

spring security MethodConfigUtils 源码

spring security MethodSecurityBeanDefinitionParser 源码

spring security MethodSecurityMetadataSourceBeanDefinitionParser 源码

spring security PointcutDelegatingAuthorizationManager 源码

spring security PrefixBasedMethodMatcher 源码

spring security package-info 源码

0  赞