Spring Security 源码理解

AbstractSecurityInterceptor 类结构图


Spring Security 源码理解_第1张图片
Security interceptors and the "secure object" model

Spring Security 源码理解_第2张图片
SecurityInterceptor UML class diagram simplified

它的三个实现类:

  1. FilterSecurityInterceptor 主要用来web请求拦截
  2. MethodSecurityInterceptor 用来方法级别的拦截
  3. AspectJSecurityInterceptor 用于aop方式的拦截
public abstract class AbstractSecurityInterceptor implements InitializingBean,
        ApplicationEventPublisherAware, MessageSourceAware {
    // ~ Static fields/initializers
    // =====================================================================================

    protected final Log logger = LogFactory.getLog(getClass());

    // ~ Instance fields
    // ================================================================================================

    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private ApplicationEventPublisher eventPublisher;
    private AccessDecisionManager accessDecisionManager;
    private AfterInvocationManager afterInvocationManager;
    private AuthenticationManager authenticationManager = new NoOpAuthenticationManager();
    private RunAsManager runAsManager = new NullRunAsManager();

    private boolean alwaysReauthenticate = false;
    private boolean rejectPublicInvocations = false;
    private boolean validateConfigAttributes = true;
    private boolean publishAuthorizationSuccess = false;
}

它的属性值:

  1. AuthenticationManager 用来认证
  2. AccessDecisionManager 用来授权
  3. SecurityMetadataSource 用来获取与 security object 相关的 ConfigAttribute
  4. AfterInvocationManager 用来 review 从 secure object invocation 返回的Object
    能够修改该 Object 或者抛出 AccessDeniedException 异常。通常用来确保允许 principal 访问服务层返回的 domain object instance
Reviews the Object returned from a secure object invocation, 
being able to modify the Object or throw an AccessDeniedException.
Typically used to ensure the principal is permitted to access the domain object instance returned
by a service layer bean. 
Can also be used to mutate the domain object instance so the principal is only able to access 
authorised bean properties or Collection elements.
  1. RunAsManager 为当前的 secure object invocation 创建一个新的临时 Authentication 对象。
    AbstractSecurityInterceptor将仅在安全对象回调的持续时间内替换SecurityContext中保存的Authentication对象

FilterSecurityInterceptor 源码

public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
...
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    FilterInvocation fi = new FilterInvocation(request, response, chain);
    this.invoke(fi);
  }
public void invoke(FilterInvocation fi) throws IOException, ServletException {
// 如果应用过该过滤器直接跳过执行下一个过滤器
    if (fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
      fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
    } else {
      if (fi.getRequest() != null) {
        fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
      }

// 1. 调用之前
      InterceptorStatusToken token = super.beforeInvocation(fi);

      try {
        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
      } finally {
// 3. 最后调用
        super.finallyInvocation(token);
      }

// 2. 调用之后
      super.afterInvocation(token, (Object)null);
    }

  }
}

beforeInvocation

protected InterceptorStatusToken beforeInvocation(Object object) {
        Assert.notNull(object, "Object was null");
        final boolean debug = logger.isDebugEnabled();
// 检查该过滤器是否能处理指定类型的对象
// 例如:FilterSecurityInterceptor 能够处理 FilterInvocation 类型的对象
// MethodSecurityInterceptor 能够处理 MethodInvocation 类型的对象
        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());
        }
// 通过 security metadata 获取与 secure object 相关的 ConfigAttribute 集合
        Collection attributes = this.obtainSecurityMetadataSource()
                .getAttributes(object);

        if (attributes == null || attributes.isEmpty()) {
            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 (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
// 通过 AccessDecisionManager 决定是否授权
        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
// 使用 run-as 功能进行,模仿另一个人获取安全权限(ROLE_RUN_AS_XXX)
        Authentication 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);
        }
// 如果方法执行到这里,并且没有异常抛出,证明 
// the invocation through to the resource. Authorization has been granted.
    }

afterInvocation

protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
        if (token == null) {
            // public object
            return returnedObject;
        }

        finallyInvocation(token); // continue to clean in this method for passivity

// 如果有 afterInvocationManager 配置它将负责处理返回值或者抛出异常
        if (afterInvocationManager != null) {
            // Attempt after invocation handling
            try {
                returnedObject = afterInvocationManager.decide(token.getSecurityContext()
                        .getAuthentication(), token.getSecureObject(), token
                        .getAttributes(), returnedObject);
            }
            catch (AccessDeniedException accessDeniedException) {
                AuthorizationFailureEvent event = new AuthorizationFailureEvent(
                        token.getSecureObject(), token.getAttributes(), token
                                .getSecurityContext().getAuthentication(),
                        accessDeniedException);
                publishEvent(event);

                throw accessDeniedException;
            }
        }

// Here is the full authorization cycled finished.
// The response is returned to the caller.
        return returnedObject;
    }

authenticateIfRequired

private Authentication authenticateIfRequired() {
        Authentication authentication = SecurityContextHolder.getContext()
                .getAuthentication();

        if (authentication.isAuthenticated() && !alwaysReauthenticate) {
            if (logger.isDebugEnabled()) {
                logger.debug("Previously Authenticated: " + authentication);
            }

            return authentication;
        }

        authentication = authenticationManager.authenticate(authentication);

        // We don't authenticated.setAuthentication(true), because each provider should do
        // that
        if (logger.isDebugEnabled()) {
            logger.debug("Successfully Authenticated: " + authentication);
        }

        SecurityContextHolder.getContext().setAuthentication(authentication);

        return authentication;
    }

Spring Security 源码理解_第3张图片
AbstractSecurityInterceptor sequence diagram simplified

你可能感兴趣的:(Spring Security 源码理解)