spring security (四) 授权基本流程

在SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验

在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后获取其中的权限信息,判断当前用户是否拥有访问当前资源所需的权限。

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 && this.observeOncePerRequest) {
            fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
        }

        InterceptorStatusToken token = super.beforeInvocation(fi);

        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.finallyInvocation(token);
        }

        super.afterInvocation(token, (Object)null);
    }

}
protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
boolean debug = this.logger.isDebugEnabled();
if (!this.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: " + this.getSecureObjectClass());
} else {
    //匹配路径
    Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
    if (attributes != null && !attributes.isEmpty()) {
        if (debug) {
            this.logger.debug("Secure object: " + object + "; Attributes: " + attributes);
        }

        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
        }

        Authentication authenticated = this.authenticateIfRequired();

        try {
            //通过传递的参数来决定用户是否有访问对应受保护资源的权限
            this.accessDecisionManager.decide(authenticated, object, attributes);
        } catch (AccessDeniedException var7) {
            this.publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, var7));
            throw var7;
        }

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

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

        Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
        if (runAs == null) {
            if (debug) {
                this.logger.debug("RunAsManager did not change Authentication object");
            }

            return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
        } else {
            if (debug) {
                this.logger.debug("Switching to RunAs Authentication: " + runAs);
            }

            SecurityContext origCtx = SecurityContextHolder.getContext();
            SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
            SecurityContextHolder.getContext().setAuthentication(runAs);
            return new InterceptorStatusToken(origCtx, true, attributes, object);
        }
    } else if (this.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'");
    } else {
        if (debug) {
            this.logger.debug("Public object - authentication not attempted");
        }

            this.publishEvent(new PublicInvocationEvent(object));
            return null;
        }
        }
        }

获取放行路径,匹配当前路径命中,这里的路径是HttpSecurity配置的放行路径;

权限控制相关API

Spring Security 匹配了URL 后调用了permitAll()表示无需授权,随意访问。在Spring Security 中提供了多种内置控制。

权限控制方法

permitAll() permitAll()表示所匹配的URL任何人都允许访问。

authenticated() authenticated()表示所匹配的URL需要被认证才能访问。

anonymous() anonymous()表示可以匿名访问匹配的URL。

denyAll() denyAll()表示所匹配的URL 都不允许被访问。

rememberMe() 被“remember me”的用户允许访问。

fullyAuthenticated() 用户通过正常登录认证的而不是被remember me 的,才可以访问

匹配的路径也是存在规则的,如下是我的配置

@Override
public void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
    .sessionManagement().disable()
    .authorizeRequests()
    .antMatchers(
        noTokenAccessUrl.toArray(new String[0])
    ).permitAll()
    .antMatchers("/**").authenticated()
    .and().headers().cacheControl();

}

访问接口区别

放行接口:

需要验证的接口:

this.accessDecisionManager.decide(authenticated, object, attributes);

通过传递的参数来决定用户是否有访问对应受保护资源的权限

这里着重说明一下decide的参数:

authentication:要访问资源的访问者的身份

object:要访问的受保护资源,web请求对应FilterInvocation

configAttributes:是受保护资源的访问策略(要访问当前资源所需要的权限),通过SecurityMetadataSource获取。 decide接口就是用来鉴定当前用户是否有访问对应受保护资源的权限。

decide接口就是用来鉴定当前用户是否有访问对应受保护资源的权限。

public class AffirmativeBased extends AbstractAccessDecisionManager {
    public AffirmativeBased(List<AccessDecisionVoter<?>> decisionVoters) {
        super(decisionVoters);
    }

    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
        int deny = 0;
        Iterator var5 = this.getDecisionVoters().iterator();

        while(var5.hasNext()) {
            AccessDecisionVoter voter = (AccessDecisionVoter)var5.next();
            //投票 1授予访问权限 0访问 弃权 -1拒绝访问
            int result = voter.vote(authentication, object, configAttributes);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Voter: " + voter + ", returned: " + result);
            }

            switch(result) {
                case -1:
                    ++deny;
                    break;
                case 1:
                    return;
            }
        }

        //拒绝数大于0
        if (deny > 0) {
            throw new AccessDeniedException(this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
        } else {
            //全是弃权的
            this.checkAllowIfAllAbstainDecisions();
        }
    }
}

投票器

WebExpressionVoter

最常用的,也是Spring Security 框架默认 FilterSecuritylnterceptor 实例中AccessDecisionManager 默认的投票器 WebExpressionVoter。其实,就是对使用http.authorizeRequests0基于Spring-EL进行控制权限的的授权决策类

public class WebExpressionVoter implements AccessDecisionVoter<FilterInvocation> {
    private SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler();

    public WebExpressionVoter() {
    }

    public int vote(Authentication authentication, FilterInvocation fi, Collection<ConfigAttribute> attributes) {
        assert authentication != null;

        assert fi != null;

        assert attributes != null;

        WebExpressionConfigAttribute weca = this.findConfigAttribute(attributes);
        if (weca == null) {
            return 0;
        } else {
            //创建评估环境 将authentication信息封装
            EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, fi);
            ctx = weca.postProcess(ctx, fi);
            return ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx) ? 1 : -1;
        }
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值