spring security MessageSecurityMetadataSourceRegistry 源码

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

spring security MessageSecurityMetadataSourceRegistry 代码

文件路径:/config/src/main/java/org/springframework/security/config/annotation/web/messaging/MessageSecurityMetadataSourceRegistry.java

/*
 * Copyright 2002-2022 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.annotation.web.messaging;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.messaging.Message;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer;
import org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler;
import org.springframework.security.messaging.access.expression.ExpressionBasedMessageSecurityMetadataSourceFactory;
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
import org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;
import org.springframework.security.messaging.util.matcher.MessageMatcher;
import org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher;
import org.springframework.security.messaging.util.matcher.SimpMessageTypeMatcher;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;

/**
 * Allows mapping security constraints using {@link MessageMatcher} to the security
 * expressions.
 *
 * @author Rob Winch
 * @since 4.0
 * @deprecated Use {@link MessageMatcherDelegatingAuthorizationManager} instead
 */
@Deprecated
public class MessageSecurityMetadataSourceRegistry {

	private static final String permitAll = "permitAll";

	private static final String denyAll = "denyAll";

	private static final String anonymous = "anonymous";

	private static final String authenticated = "authenticated";

	private static final String fullyAuthenticated = "fullyAuthenticated";

	private static final String rememberMe = "rememberMe";

	private SecurityExpressionHandler<Message<Object>> expressionHandler = new DefaultMessageSecurityExpressionHandler<>();

	private final LinkedHashMap<MatcherBuilder, String> matcherToExpression = new LinkedHashMap<>();

	private DelegatingPathMatcher pathMatcher = new DelegatingPathMatcher();

	private boolean defaultPathMatcher = true;

	/**
	 * Maps any {@link Message} to a security expression.
	 * @return the Expression to associate
	 */
	public Constraint anyMessage() {
		return matchers(MessageMatcher.ANY_MESSAGE);
	}

	/**
	 * Maps any {@link Message} that has a null SimpMessageHeaderAccessor destination
	 * header (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, UNSUBSCRIBE, DISCONNECT,
	 * DISCONNECT_ACK, OTHER)
	 * @return the Expression to associate
	 */
	public Constraint nullDestMatcher() {
		return matchers(SimpDestinationMessageMatcher.NULL_DESTINATION_MATCHER);
	}

	/**
	 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances.
	 * @param typesToMatch the {@link SimpMessageType} instance to match on
	 * @return the {@link Constraint} associated to the matchers.
	 */
	public Constraint simpTypeMatchers(SimpMessageType... typesToMatch) {
		MessageMatcher<?>[] typeMatchers = new MessageMatcher<?>[typesToMatch.length];
		for (int i = 0; i < typesToMatch.length; i++) {
			SimpMessageType typeToMatch = typesToMatch[i];
			typeMatchers[i] = new SimpMessageTypeMatcher(typeToMatch);
		}
		return matchers(typeMatchers);
	}

	/**
	 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances without
	 * regard to the {@link SimpMessageType}. If no destination is found on the Message,
	 * then the Matcher returns false.
	 * @param patterns the patterns to create
	 * {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
	 * from. Uses
	 * {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)} .
	 * @return the {@link Constraint} that is associated to the {@link MessageMatcher}
	 * @see MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)
	 */
	public Constraint simpDestMatchers(String... patterns) {
		return simpDestMatchers(null, patterns);
	}

	/**
	 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that match
	 * on {@code SimpMessageType.MESSAGE}. If no destination is found on the Message, then
	 * the Matcher returns false.
	 * @param patterns the patterns to create
	 * {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
	 * from. Uses
	 * {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}.
	 * @return the {@link Constraint} that is associated to the {@link MessageMatcher}
	 * @see MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)
	 */
	public Constraint simpMessageDestMatchers(String... patterns) {
		return simpDestMatchers(SimpMessageType.MESSAGE, patterns);
	}

	/**
	 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances that match
	 * on {@code SimpMessageType.SUBSCRIBE}. If no destination is found on the Message,
	 * then the Matcher returns false.
	 * @param patterns the patterns to create
	 * {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
	 * from. Uses
	 * {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}.
	 * @return the {@link Constraint} that is associated to the {@link MessageMatcher}
	 * @see MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)
	 */
	public Constraint simpSubscribeDestMatchers(String... patterns) {
		return simpDestMatchers(SimpMessageType.SUBSCRIBE, patterns);
	}

	/**
	 * Maps a {@link List} of {@link SimpDestinationMessageMatcher} instances. If no
	 * destination is found on the Message, then the Matcher returns false.
	 * @param type the {@link SimpMessageType} to match on. If null, the
	 * {@link SimpMessageType} is not considered for matching.
	 * @param patterns the patterns to create
	 * {@link org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher}
	 * from. Uses
	 * {@link MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)}.
	 * @return the {@link Constraint} that is associated to the {@link MessageMatcher}
	 * @see MessageSecurityMetadataSourceRegistry#simpDestPathMatcher(PathMatcher)
	 */
	private Constraint simpDestMatchers(SimpMessageType type, String... patterns) {
		List<MatcherBuilder> matchers = new ArrayList<>(patterns.length);
		for (String pattern : patterns) {
			matchers.add(new PathMatcherMessageMatcherBuilder(pattern, type));
		}
		return new Constraint(matchers);
	}

	/**
	 * The {@link PathMatcher} to be used with the
	 * {@link MessageSecurityMetadataSourceRegistry#simpDestMatchers(String...)}. The
	 * default is to use the default constructor of {@link AntPathMatcher}.
	 * @param pathMatcher the {@link PathMatcher} to use. Cannot be null.
	 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
	 * customization.
	 */
	public MessageSecurityMetadataSourceRegistry simpDestPathMatcher(PathMatcher pathMatcher) {
		Assert.notNull(pathMatcher, "pathMatcher cannot be null");
		this.pathMatcher.setPathMatcher(pathMatcher);
		this.defaultPathMatcher = false;
		return this;
	}

	/**
	 * Determines if the {@link #simpDestPathMatcher(PathMatcher)} has been explicitly
	 * set.
	 * @return true if {@link #simpDestPathMatcher(PathMatcher)} has been explicitly set,
	 * else false.
	 */
	protected boolean isSimpDestPathMatcherConfigured() {
		return !this.defaultPathMatcher;
	}

	/**
	 * Maps a {@link List} of {@link MessageMatcher} instances to a security expression.
	 * @param matchers the {@link MessageMatcher} instances to map.
	 * @return The {@link Constraint} that is associated to the {@link MessageMatcher}
	 * instances
	 */
	public Constraint matchers(MessageMatcher<?>... matchers) {
		List<MatcherBuilder> builders = new ArrayList<>(matchers.length);
		for (MessageMatcher<?> matcher : matchers) {
			builders.add(new PreBuiltMatcherBuilder(matcher));
		}
		return new Constraint(builders);
	}

	/**
	 * The {@link SecurityExpressionHandler} to be used. The default is to use
	 * {@link DefaultMessageSecurityExpressionHandler}.
	 * @param expressionHandler the {@link SecurityExpressionHandler} to use. Cannot be
	 * null.
	 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
	 * customization.
	 */
	public MessageSecurityMetadataSourceRegistry expressionHandler(
			SecurityExpressionHandler<Message<Object>> expressionHandler) {
		Assert.notNull(expressionHandler, "expressionHandler cannot be null");
		this.expressionHandler = expressionHandler;
		return this;
	}

	/**
	 * Allows subclasses to create creating a {@link MessageSecurityMetadataSource}.
	 *
	 * <p>
	 * This is not exposed so as not to confuse users of the API, which should never
	 * invoke this method.
	 * </p>
	 * @return the {@link MessageSecurityMetadataSource} to use
	 */
	protected MessageSecurityMetadataSource createMetadataSource() {
		LinkedHashMap<MessageMatcher<?>, String> matcherToExpression = new LinkedHashMap<>();
		for (Map.Entry<MatcherBuilder, String> entry : this.matcherToExpression.entrySet()) {
			matcherToExpression.put(entry.getKey().build(), entry.getValue());
		}
		return ExpressionBasedMessageSecurityMetadataSourceFactory
				.createExpressionMessageMetadataSource(matcherToExpression, this.expressionHandler);
	}

	/**
	 * Allows determining if a mapping was added.
	 *
	 * <p>
	 * This is not exposed so as not to confuse users of the API, which should never need
	 * to invoke this method.
	 * </p>
	 * @return true if a mapping was added, else false
	 */
	protected boolean containsMapping() {
		return !this.matcherToExpression.isEmpty();
	}

	private static String hasAnyRole(String... authorities) {
		String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','ROLE_");
		return "hasAnyRole('ROLE_" + anyAuthorities + "')";
	}

	private static String hasRole(String role) {
		Assert.notNull(role, "role cannot be null");
		if (role.startsWith("ROLE_")) {
			throw new IllegalArgumentException(
					"role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
		}
		return "hasRole('ROLE_" + role + "')";
	}

	private static String hasAuthority(String authority) {
		return "hasAuthority('" + authority + "')";
	}

	private static String hasAnyAuthority(String... authorities) {
		String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','");
		return "hasAnyAuthority('" + anyAuthorities + "')";
	}

	/**
	 * Represents the security constraint to be applied to the {@link MessageMatcher}
	 * instances.
	 */
	public final class Constraint {

		private final List<? extends MatcherBuilder> messageMatchers;

		/**
		 * Creates a new instance
		 * @param messageMatchers the {@link MessageMatcher} instances to map to this
		 * constraint
		 */
		private Constraint(List<? extends MatcherBuilder> messageMatchers) {
			Assert.notEmpty(messageMatchers, "messageMatchers cannot be null or empty");
			this.messageMatchers = messageMatchers;
		}

		/**
		 * Shortcut for specifying {@link Message} instances require a particular role. If
		 * you do not want to have "ROLE_" automatically inserted see
		 * {@link #hasAuthority(String)}.
		 * @param role the role to require (i.e. USER, ADMIN, etc). Note, it should not
		 * start with "ROLE_" as this is automatically inserted.
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 */
		public MessageSecurityMetadataSourceRegistry hasRole(String role) {
			return access(MessageSecurityMetadataSourceRegistry.hasRole(role));
		}

		/**
		 * Shortcut for specifying {@link Message} instances require any of a number of
		 * roles. If you do not want to have "ROLE_" automatically inserted see
		 * {@link #hasAnyAuthority(String...)}
		 * @param roles the roles to require (i.e. USER, ADMIN, etc). Note, it should not
		 * start with "ROLE_" as this is automatically inserted.
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 */
		public MessageSecurityMetadataSourceRegistry hasAnyRole(String... roles) {
			return access(MessageSecurityMetadataSourceRegistry.hasAnyRole(roles));
		}

		/**
		 * Specify that {@link Message} instances require a particular authority.
		 * @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 */
		public MessageSecurityMetadataSourceRegistry hasAuthority(String authority) {
			return access(MessageSecurityMetadataSourceRegistry.hasAuthority(authority));
		}

		/**
		 * Specify that {@link Message} instances requires any of a number authorities.
		 * @param authorities the requests require at least one of the authorities (i.e.
		 * "ROLE_USER","ROLE_ADMIN" would mean either "ROLE_USER" or "ROLE_ADMIN" is
		 * required).
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 */
		public MessageSecurityMetadataSourceRegistry hasAnyAuthority(String... authorities) {
			return access(MessageSecurityMetadataSourceRegistry.hasAnyAuthority(authorities));
		}

		/**
		 * Specify that Messages are allowed by anyone.
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 */
		public MessageSecurityMetadataSourceRegistry permitAll() {
			return access(permitAll);
		}

		/**
		 * Specify that Messages are allowed by anonymous users.
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 */
		public MessageSecurityMetadataSourceRegistry anonymous() {
			return access(anonymous);
		}

		/**
		 * Specify that Messages are allowed by users that have been remembered.
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 * @see RememberMeConfigurer
		 */
		public MessageSecurityMetadataSourceRegistry rememberMe() {
			return access(rememberMe);
		}

		/**
		 * Specify that Messages are not allowed by anyone.
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 */
		public MessageSecurityMetadataSourceRegistry denyAll() {
			return access(denyAll);
		}

		/**
		 * Specify that Messages are allowed by any authenticated user.
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 */
		public MessageSecurityMetadataSourceRegistry authenticated() {
			return access(authenticated);
		}

		/**
		 * Specify that Messages are allowed by users who have authenticated and were not
		 * "remembered".
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 * @see RememberMeConfigurer
		 */
		public MessageSecurityMetadataSourceRegistry fullyAuthenticated() {
			return access(fullyAuthenticated);
		}

		/**
		 * Allows specifying that Messages are secured by an arbitrary expression
		 * @param attribute the expression to secure the URLs (i.e. "hasRole('ROLE_USER')
		 * and hasRole('ROLE_SUPER')")
		 * @return the {@link MessageSecurityMetadataSourceRegistry} for further
		 * customization
		 */
		public MessageSecurityMetadataSourceRegistry access(String attribute) {
			for (MatcherBuilder messageMatcher : this.messageMatchers) {
				MessageSecurityMetadataSourceRegistry.this.matcherToExpression.put(messageMatcher, attribute);
			}
			return MessageSecurityMetadataSourceRegistry.this;
		}

	}

	private static final class PreBuiltMatcherBuilder implements MatcherBuilder {

		private MessageMatcher<?> matcher;

		private PreBuiltMatcherBuilder(MessageMatcher<?> matcher) {
			this.matcher = matcher;
		}

		@Override
		public MessageMatcher<?> build() {
			return this.matcher;
		}

	}

	private final class PathMatcherMessageMatcherBuilder implements MatcherBuilder {

		private final String pattern;

		private final SimpMessageType type;

		private PathMatcherMessageMatcherBuilder(String pattern, SimpMessageType type) {
			this.pattern = pattern;
			this.type = type;
		}

		@Override
		public MessageMatcher<?> build() {
			if (this.type == null) {
				return new SimpDestinationMessageMatcher(this.pattern,
						MessageSecurityMetadataSourceRegistry.this.pathMatcher);
			}
			if (SimpMessageType.MESSAGE == this.type) {
				return SimpDestinationMessageMatcher.createMessageMatcher(this.pattern,
						MessageSecurityMetadataSourceRegistry.this.pathMatcher);
			}
			if (SimpMessageType.SUBSCRIBE == this.type) {
				return SimpDestinationMessageMatcher.createSubscribeMatcher(this.pattern,
						MessageSecurityMetadataSourceRegistry.this.pathMatcher);
			}
			throw new IllegalStateException(this.type + " is not supported since it does not have a destination");
		}

	}

	private interface MatcherBuilder {

		MessageMatcher<?> build();

	}

	static class DelegatingPathMatcher implements PathMatcher {

		private PathMatcher delegate = new AntPathMatcher();

		@Override
		public boolean isPattern(String path) {
			return this.delegate.isPattern(path);
		}

		@Override
		public boolean match(String pattern, String path) {
			return this.delegate.match(pattern, path);
		}

		@Override
		public boolean matchStart(String pattern, String path) {
			return this.delegate.matchStart(pattern, path);
		}

		@Override
		public String extractPathWithinPattern(String pattern, String path) {
			return this.delegate.extractPathWithinPattern(pattern, path);
		}

		@Override
		public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
			return this.delegate.extractUriTemplateVariables(pattern, path);
		}

		@Override
		public Comparator<String> getPatternComparator(String path) {
			return this.delegate.getPatternComparator(path);
		}

		@Override
		public String combine(String pattern1, String pattern2) {
			return this.delegate.combine(pattern1, pattern2);
		}

		void setPathMatcher(PathMatcher pathMatcher) {
			this.delegate = pathMatcher;
		}

	}

}

相关信息

spring security 源码目录

相关文章

spring security AclEntryVoter 源码

spring security AclPermissionCacheOptimizer 源码

spring security AclPermissionEvaluator 源码

spring security AbstractAclProvider 源码

spring security AclEntryAfterInvocationCollectionFilteringProvider 源码

spring security AclEntryAfterInvocationProvider 源码

spring security ArrayFilterer 源码

spring security CollectionFilterer 源码

spring security Filterer 源码

spring security package-info 源码

0  赞