1. ホーム
  2. Java

SpringSecurityで公開インターフェースカスタム権限検証失敗問題を解決、ソースコード解析も

2022-02-24 17:13:34
<パス <ブロッククオート

背景: カスタム権限認証、インターフェースの一部は適切なロール権限を持っている必要があり、インターフェースの一部はすべての訪問者に公開され、インターフェースの一部は誰からもアクセスできないようにします。しかし、SpringSecurityを使う過程で、フレームワークから URL resource を傍受することなく、直接

ユーザーログインの認証に成功した後、キャリー Token にアクセスしてください。 URL resources を使用すると、春のセキュリティは Token (in request header Authorization) を使用して、異なるユーザーを区別することができます。

ユーザー権限のデータソースは Map を使用します。 URL resource をキーとし List of roles with access rights to the Key は値。

使用したところ、インターフェイスに Key があっても Value が null や空の場合、spring security framework が自動的に解放してしまい、パーミッションに失敗する問題が発生することが判明しました。

これを解決する方法は2つあります。

第一の方法
デフォルトのrejectPublicInvocationsはfalseです。
パーミッションの制御が必要な方へ URL resources roleListが空で権限検証をスキップしないようにするフラグを追加する。
パブリックパーミッションをNULLに設定し、パーミッションの検証を行わない。

<ブロッククオート

2つ目の方法
rejectPublicInvocationsをtrueに設定する。
この後、roleListは空か見つからない。 URL resource の場合、アクセスが拒否されます。
コントロールパーミッションを必要とするもの URL resources 対応するロールが空であっても、パーミッションは検証されます。
公開許可は、許可検証を行わずにすべてのロールおよび匿名ロールに設定される

package org.springframework.security.access.intercept;
/**
 * Source code for an abstract class for intercepting security objects (access requests + user bodies)
 */
public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {

	// ... Other methods omitted
	
	protected InterceptorStatusToken beforeInvocation(Object object) {
		Assert.notNull(object, "Object was null");
		final boolean debug = logger.isDebugEnabled();

		if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
			throw new IllegalArgumentException(
					"Security invocation attempted for object "
							+ object.getClass().getName()
							+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
							+ getSecureObjectClass());
		}

		// Gets the current 
 attributes = this.obtainSecurityMetadataSource().getAttributes(object);
				
		// The framework determines here if the list of roles corresponding to the URL resource is empty
		if (attributes == null || attributes.isEmpty()) {
			// rejectPublicInvocations defaults to false 
			// can be configured to true, i.e. no release if the role list is empty
			if (rejectPublicInvocations) {
				throw new IllegalArgumentException(
						"Secure object invocation "
								+ object
								+ " was denied as public invocations are not allowed via this interceptor. "
								+ "This indicates a configuration error because the "
								+ "rejectPublicInvocations property is set to 'true'");
			}

			if (debug) {
				logger.debug("Public object - authentication not attempted");
			}

			publishEvent(new PublicInvocationEvent(object));

			return null; // no further work post-invocation
		}

		if (debug) {
			logger.debug("Secure object: " + object + "; Attributes: " + attributes);
		}
		
		// If the current user permission object is null
		if (SecurityContextHolder.getContext().getAuthentication() == null) {
			credentialsNotFound(messages.getMessage(
					"AbstractSecurityInterceptor.authenticationNotFound",
					"An Authentication object was not found in the SecurityContext"),
					object, attributes);
		}

		Authentication authenticated = authenticateIfRequired();

		// Attempt authorization, where accessDecisionManager is called for authentication
		try {
			this.accessDecisionManager.decide(authenticated, object, attributes);
		}
		catch (AccessDeniedException accessDeniedException) {
			publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
					accessDeniedException));

			throw accessDeniedException;
		}

		if (debug) {
			logger.debug("Authorization successful");
		}

		if (publishAuthorizationSuccess) {
			publishEvent(new AuthorizedEvent(object, attributes, authenticated));
		}

		// Attempt to run as a different user, where you can additionally configure or modify the user's permission object for special scenarios
		Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
				runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);

		if (runAs == null) {
			if (debug) {
				logger.debug("RunAsManager did not change Authentication object");
			}

			// no further work post-invocation
			return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,
					attributes, object);
		}
		else {
			if (debug) {
				logger.debug("Switching to RunAs Authentication: " + runAs);
			}

			SecurityContext origCtx = SecurityContextHolder.getContext();
			SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
			SecurityContextHolder.getContext().setAuthentication(runAs);

			// need to revert to token.Authenticated post-invocation
			return new InterceptorStatusToken(origCtx, true, attributes, object);
		}
	}
	// ... Other methods are omitted
}