那些源码里的设计模式(一)选举模式

175 阅读2分钟

模式简介

选举模式是随便起的一个名字,因为源码里没看到给他起名字。

它用于对一个数据项进行是否通过的表决,其中表决类可能有多个。源码中的场景主要是下面两个:Spring Security里的身份认证,Spring Security里的资源授权认证。

源码中的场景详情

Spring Security里的资源最小粒度可以到方法级别,如@PreAuthorize加在方法上,通过spring表达式判断当前登录人是否有访问权限。这里判断的核心类是accessDecisionManager,他的初始化定义在GlobalMethodSecurityConfiguration中。

AccessDecisionManager是核心接口,他的核心方法是decide,即判断是否有权限。

AbstractAccessDecisionManager是默认抽象实现,这里用了模板方法,也是源码中常见处理手段(更标准化的模板模式应用可以看OncePerRequestFilter)。抽象类里定义了一个成员voter列表,这个对象表示所有投票该登录人是否可以访问该资源的列表。

下面是他的UML图。

image.png

最下面的三个实现类中分别代表三类逻辑:

  • 只要有一个voter通过就可以访问
  • 所有voter都通过才可以访问
  • 通过的大于不通过的就可以访问

举第一个例子,也是spring security里的默认实现

public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException {
        int deny = 0;
        for (AccessDecisionVoter voter : getDecisionVoters()) {
                int result = voter.vote(authentication, object, configAttributes);
                switch (result) {
                case AccessDecisionVoter.ACCESS_GRANTED:
                        return;  // 一个同意就直接return了
                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();
}

其他几个实现类逻辑类似。

AuthenticationManager的子类ProviderManager和上面的一个通过则通过比较像,他没有提供其他类型的选举,但是提供了其他类型的子类,包括OAuth2AuthenticationManager、委派Manager等。

实际应用

实际应用场景主要是一些组合判断。上述场景中的一个过就过和所有过才过,其实也对应了算数表达式中的||和&&。