• Springsecurity源码注解权限原理之MethodSecurityInterceptor(二十一)


    MethodSecurityInterceptor是Spring Security对于Spring Aop的切入逻辑

     @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            //<1>调用方法前的权限校验
            InterceptorStatusToken token = super.beforeInvocation(mi);
            Object result;
            try {
                result = mi.proceed();
            }
            finally {
                //允许重置ServletContext
                super.finallyInvocation(token);
            }
            //<5>调用方法后的权限校验
            return super.afterInvocation(token, result);
        }

    <1>

    org.springframework.security.access.intercept.AbstractSecurityInterceptor#beforeInvocation

        protected InterceptorStatusToken beforeInvocation(Object object) {
            Assert.notNull(object, "Object was null");
            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());
            }
            //<2>基于object获取对应的ConfigAttribute 也就是我们的权限配置 初始化处参考
            Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
            //如果配有配置表示不需要权限校验
            if (CollectionUtils.isEmpty(attributes)) {
                Assert.isTrue(!this.rejectPublicInvocations,
                        () -> "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 (this.logger.isDebugEnabled()) {
                    this.logger.debug(LogMessage.format("Authorized public object %s", object));
                }
                publishEvent(new PublicInvocationEvent(object));
                return null; // no further work post-invocation
            }
            //如果未登录 抛出异常
            if (SecurityContextHolder.getContext().getAuthentication() == null) {
                credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
                        "An Authentication object was not found in the SecurityContext"), object, attributes);
            }
            //如果登录过期触发自动authenticationManager.authenticate 重新认证
            Authentication authenticated = authenticateIfRequired();
            if (this.logger.isTraceEnabled()) {
                this.logger.trace(LogMessage.format("Authorizing %s with attributes %s", object, attributes));
            }
            // <3>现在有方法 和我们的权限配置则校验授权
            attemptAuthorization(object, attributes, authenticated);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug(LogMessage.format("Authorized %s with attributes %s", object, attributes));
            }
            if (this.publishAuthorizationSuccess) {
                publishEvent(new AuthorizedEvent(object, attributes, authenticated));
            }
    
            // Attempt to run as a different user
            Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
            if (runAs != null) {
                SecurityContext origCtx = SecurityContextHolder.getContext();
                SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
                SecurityContextHolder.getContext().setAuthentication(runAs);
    
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug(LogMessage.format("Switched to RunAs authentication %s", runAs));
                }
                // need to revert to token.Authenticated post-invocation
                return new InterceptorStatusToken(origCtx, true, attributes, object);
            }
            this.logger.trace("Did not switch RunAs authentication since RunAsManager returned null");
            // no further work post-invocation
            return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
    
        }

    <2>

    org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource#getAttributes

    @Override
        public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
            //方法和class作为cacheKey
            DelegatingMethodSecurityMetadataSource.DefaultCacheKey cacheKey = new DelegatingMethodSecurityMetadataSource.DefaultCacheKey(method, targetClass);
            //同步锁
            synchronized (this.attributeCache) {
                //先判断cache是否有
                Collection<ConfigAttribute> cached = this.attributeCache.get(cacheKey);
                // 如果有直接返回
                if (cached != null) {
                    return cached;
                }
                //遍历MethodSecurityMetadataSource 解析注解获得ConfigAttribute
                Collection<ConfigAttribute> attributes = null;
                for (MethodSecurityMetadataSource s : this.methodSecurityMetadataSources) {
                    attributes = s.getAttributes(method, targetClass);
                    if (attributes != null && !attributes.isEmpty()) {
                        break;
                    }
                }
                // Put it in the cache.
                if (attributes == null || attributes.isEmpty()) {
                    this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
                    return NULL_CONFIG_ATTRIBUTE;
                }
                this.attributeCache.put(cacheKey, attributes);
                return attributes;
            }
        }

    <3>

    org.springframework.security.access.intercept.AbstractSecurityInterceptor#attemptAuthorization

     private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,
                                          Authentication authenticated) {
            try {
                //<4>交给accessDecisionManager处理 初始化处参考
                this.accessDecisionManager.decide(authenticated, object, attributes);
            }
            catch (AccessDeniedException ex) {//针对授权不过的抛出异常
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace(LogMessage.format("Failed to authorize %s with attributes %s using %s", object,
                            attributes, this.accessDecisionManager));
                }
                else if (this.logger.isDebugEnabled()) {
                    this.logger.debug(LogMessage.format("Failed to authorize %s with attributes %s", object, attributes));
                }
                publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex));
                throw ex;
            }
        }

    <4>

     @Override
        @SuppressWarnings({ "rawtypes", "unchecked" })
        public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
                throws AccessDeniedException {
            int deny = 0;
            //遍历voter 调用vote方法进行处理
            for (AccessDecisionVoter voter : getDecisionVoters()) {
                int result = voter.vote(authentication, object, configAttributes);
                switch (result) {
                    //授权通过直接返回
                    case AccessDecisionVoter.ACCESS_GRANTED:
                        return;
                        //针对授权不过 返回ACCESS_DENIED
                    case AccessDecisionVoter.ACCESS_DENIED:
                        deny++;
                        break;
                    default:
                        break;
                }
            }
            if (deny > 0) {
                throw new AccessDeniedException(
                        this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
            }
            // To get this far, every AccessDecisionVoter abstained
            checkAllowIfAllAbstainDecisions();
        }

    <5>

        protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
            if (token == null) {
                // public object
                return returnedObject;
            }
            finallyInvocation(token); // continue to clean in this method for passivity
            if (this.afterInvocationManager != null) {
                // Attempt after invocation handling
                try {
                    returnedObject = this.afterInvocationManager.decide(token.getSecurityContext().getAuthentication(),
                            token.getSecureObject(), token.getAttributes(), returnedObject);
                }
                catch (AccessDeniedException ex) {
                    publishEvent(new AuthorizationFailureEvent(token.getSecureObject(), token.getAttributes(),
                            token.getSecurityContext().getAuthentication(), ex));
                    throw ex;
                }
            }
            return returnedObject;
        }
  • 相关阅读:
    微信公众平台订阅号和服务号和企业号的区别
    微信支付现金红包接口说明及应用实例代码
    Android开发环境配置
    在自己的android工程中使用actionbarsherlock以及slidingmenu
    android个推推送平台的使用
    android网络
    android String 类型转换成UTF-8格式
    【转】android神一样的模拟器——genymotion
    基于百度云推送的实时通信客户端实现(三)
    基于百度云推送的实时通信客户端实现(二)
  • 原文地址:https://www.cnblogs.com/LQBlog/p/15556251.html
Copyright © 2020-2023  润新知