Spring Security 授权体系结构

目录

1、Authorities 授权(AuthorizationFilter 过滤器)

2、AuthorizationManager 授权管理器

3、角色的层次化(Roles)


1、Authorities 授权(AuthorizationFilter 过滤器)

        通过 Authentication.getAuthorities() 可获取已通过验证的用户授权信息(GrantedAuthority),GrantedAuthority 对象由 AuthenticationManager 插入到 Authentication 对象中,然后在做出授权决策时由 AuthorizationManager 实例读取

        AuthenticationManager 的常用实现为 ProviderManager,ProviderManager.authenticate 在进行身份验证完成后,会填充 Authentication 对象,其中就包括对象的授权信息(GrantedAuthority)。

//具体授权的Provider列表
private List<AuthenticationProvider> providers;

//授权方法
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        //省略...

        //1-定义身份验证对象
        Authentication result = null;
        Authentication parentResult = null;
        int currentPosition = 0;
        int size = this.providers.size();
        Iterator var9 = this.getProviders().iterator();

        while(var9.hasNext()) {
            AuthenticationProvider provider = (AuthenticationProvider)var9.next();
            if (provider.supports(toTest)) {
                //省略...

                try {

                    //2-通过用户验证后,在这里返回填充好的Authentication对象
                    result = provider.authenticate(authentication);

                    if (result != null) {
                        this.copyDetails(authentication, result);
                        break;
                    }
                } catch (InternalAuthenticationServiceException | AccountStatusException var14) {
                    this.prepareException(var14, authentication);
                    throw var14;
                } catch (AuthenticationException var15) {
                    lastException = var15;
                }
            }
        }

        //省略...

        if (result != null) {
            if (this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) {
                ((CredentialsContainer)result).eraseCredentials();
            }

            if (parentResult == null) {
                this.eventPublisher.publishAuthenticationSuccess(result);
            }
            //3-在这里将封装的对象返回
            return result;
        } else {
            if (lastException == null) {
                lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[]{toTest.getName()}, "No AuthenticationProvider found for {0}"));
            }

            if (parentException == null) {
                this.prepareException((AuthenticationException)lastException, authentication);
            }
            throw lastException;
        }
    }

        ​​​AuthenticationProvider 的实现有很多,这里使用常用的 AbstractUserDetailsAuthenticationProvider 来看一下用户权限的填充步骤。

    //AbstractUserDetailsAuthenticationProvider.authenticate方法
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        //省略...

        //1-获取用户名称
        String username = this.determineUsername(authentication);

        //2-从缓存中获取用户信息(UserDetails)
        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);
        if (user == null) {
            cacheWasUsed = false;

            try {
                //3-缓存中无用户信息,再去查找其他UserDetails的实现
                user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
            } catch (UsernameNotFoundException var6) {
                //错误处理,省略...
        }

        //省略..

        //4-最后这个返回,就是返回封装好的Authentication对象
        return this.createSuccessAuthentication(principalToReturn, authentication, user);
    }

    //封装Authentication对象
    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {

        //在这里设置了用户的权限->user.getAuthorities() -> GrantedAuthority
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));

        result.setDetails(authentication.getDetails());
        this.logger.debug("Authenticated user");
        return result;
    }

        用户权限填充完了,什么时候取用呢?权限信息由授权过滤器 AuthorizationFilter 在进行授权的时候取用。

//授权过滤器
public class AuthorizationFilter extends OncePerRequestFilter {

    //授权管理器
    private final AuthorizationManager<HttpServletRequest> authorizationManager;

    public AuthorizationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
        Assert.notNull(authorizationManager, "authorizationManager cannot be null");
        this.authorizationManager = authorizationManager;
    }

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        // verify方法进行授权验证,this::getAuthentication 获取权限信息
        this.authorizationManager.verify(this::getAuthentication, request);

        filterChain.doFilter(request, response);
    }

    private Authentication getAuthentication() {
        //从SecurityContextHolder中获取权限信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication == null) {
            throw new AuthenticationCredentialsNotFoundException("An Authentication object was not found in the SecurityContext");
        } else {
            return authentication;
        }
    }
}

2、AuthorizationManager 授权管理器

        AuthorizationManager,用来确定 Authentication 是否有权访问特定对象。

        在 Spring Security 中 AuthorizationManager 取代了 AccessDecisionManager AccessDecisionVoter

扫描二维码关注公众号,回复: 16430335 查看本文章

        Spring 官方鼓励自定义 AccessDecisionManager 或 AccessDecisionVoter 的应用程序改为使用 AuthorizationManager

        AuthorizationManager 由 AuthorizationFilter 调用,负责做出最终的访问控制决策。AuthorizationManager 接口包含两个方法://函数式接口

@FunctionalInterface
public interface AuthorizationManager<T> {

    //确定是否为特定Authentication或对象授予访问权限。
    default void verify(Supplier<Authentication> authentication, T object) {
        AuthorizationDecision decision = this.check(authentication, object);
        if (decision != null && !decision.isGranted()) {
            throw new AccessDeniedException("Access Denied");
        }
    }

    //确定是否为特定Authentication或对象授予访问权限。
    @Nullable
    AuthorizationDecision check(Supplier<Authentication> var1, T var2);
}

        用户可以通过实现 AuthorizationManager 接口来自定义授权控制,同时,Spring Security 附带了一个委托 AuthorizationManager,它可以与各个 AuthorizationManager 进行协作。

        RequestMatcherDelegatingAuthorizationManager 将选择最合适的 AuthorizationManager 匹配请求。对于方法的安全控制,可以使用 AuthorizationManagerBeforeMethodInterceptor AuthorizationManagerAfterMethodInterceptor 进行实现。//注意,这两个拦截器在5.6后的版本中才有

    //请求匹配器和授权管理器的集合
    private final Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings;  
  
    //RequestMatcherDelegatingAuthorizationManager.check方法
    public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(LogMessage.format("Authorizing %s", request));
        }
        //1-获取请求匹配器和授权管理器集合的迭代器
        Iterator var3 = this.mappings.entrySet().iterator();

        Entry mapping;
        MatchResult matchResult;
        do {
            if (!var3.hasNext()) {
                this.logger.trace("Abstaining since did not find matching RequestMatcher");
                return null;
            }
            //2-获取一个Entry
            mapping = (Entry)var3.next();
            //3-获取一个RequestMatcher 
            RequestMatcher matcher = (RequestMatcher)mapping.getKey();
            //4-进行匹配
            matchResult = matcher.matcher(request);
        } while(!matchResult.isMatch());

        //5-如果匹配成功,会选择一个对应的授权器:AuthorizationManager
        AuthorizationManager<RequestAuthorizationContext> manager = (AuthorizationManager)mapping.getValue();
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
        }

        //6-选择特定的授权管理器进行授权操作
        return manager.check(authentication, new RequestAuthorizationContext(request, matchResult.getVariables()));
    }

        AuthorizationManagerBeforeMethodInterceptor: 是一个方法拦截器,它使用 AuthorizationManager 来确定一个 Authentication 是否可以调用给定的 MethodInvocation。//该拦截器在方法调用前进行拦截

    //授权管理器
    private final AuthorizationManager<MethodInvocation> authorizationManager;    
    
    //AuthorizationManagerBeforeMethodInterceptor.attemptAuthorization方法
    //在该拦截器中尝试使用授权管理器进行授权
    private void attemptAuthorization(MethodInvocation mi) {
        this.logger.debug(LogMessage.of(() -> {
            return "Authorizing method invocation " + mi;
        }));
        //1-执行授权操作
        AuthorizationDecision decision = this.authorizationManager.check(this.authentication, mi);

        //2-发布授权事件(Authentication对象,调用方法,授权结果)
        this.eventPublisher.publishAuthorizationEvent(this.authentication, mi, decision);

        if (decision != null && !decision.isGranted()) {

            //3-授权失败,记录日志,抛出异常
            this.logger.debug(LogMessage.of(() -> {
                return "Failed to authorize " + mi + " with authorization manager " + this.authorizationManager + " and decision " + decision;
            }));
            throw new AccessDeniedException("Access Denied");
        } else {

             //4-授权成功,记录日志,后续执行指定方法
            this.logger.debug(LogMessage.of(() -> {
                return "Authorized method invocation " + mi;
            }));
        }
    }

    //AuthorizationManagerBeforeMethodInterceptor.invoke方法
    public Object invoke(MethodInvocation mi) throws Throwable {
        //1-调用拦截器进行授权
        this.attemptAuthorization(mi);

        //2-授权成功后执行指定方法(此时授权没有抛出异常)
        return mi.proceed();
    }

        对应的授权管理器,Spring Security 提供了指定的授权器 PreAuthorizeAuthorizationManager,在该授权器中执行授权相关操作。

        AuthorizationManagerAfterMethodInterceptor也是一个MethodInterceptor,它可以使用 AuthorizationManager 来确定一个 Authentication 是否可以访问一个 MethodInvocation 的结果。//这个拦截器是在方法调用结束后进行拦截

        授权管理器(AuthorizationManager)的实现类图示:

3、角色的层次化(Roles)

        应用程序中的尝尝指定一个特定的角色会自动“包含”其他的角色。例如,在具有“Admin”和“User”角色概念的应用程序中,希望管理员(“Admin”)能够执行普通用户(“User”)的所有操作。要实现这一需求,就要确保所有管理员角色也被分配了“User”角色。

        角色层次结构允许配置一个角色(或权限)时可以包含其他角色。Spring Security 的 RoleVoter 的扩展版本 RoleHierarchyVoter 配置了一个 RoleHierarchy,它从中获得分配给用户的所有“可访问权限”。//角色选择器

        典型的配置可能是这样的:

@Bean
AccessDecisionVoter hierarchyVoter() {
    RoleHierarchy hierarchy = new RoleHierarchyImpl();
    hierarchy.setHierarchy("ROLE_ADMIN > ROLE_STAFF\n" +
            "ROLE_STAFF > ROLE_USER\n" +
            "ROLE_USER > ROLE_GUEST");
    return new RoleHierarchyVoter(hierarchy);
}

        在上边代码中定义了四个层次结构的角色:

ROLE_ADMIN ⇒ ROLE_STAFF ⇒ ROLE_USER ⇒ ROLE_GUEST

        使用 ROLE_ADMIN 角色进行身份验证的用户,会同时拥有其他角色的权限,其中 ">" 可以被看成”包括“的意思。

        注意:RoleHierarchy bean 配置还没有移植到 @EnableMethodSecurity。因此,本例使用的是 AccessDecisionVoter。如果需要 RoleHierarchy 来支持方法的安全性,请继续使用@EnableGlobalMethodSecurity,直到这个bug修复完成。//这个配置是有bug的,后续应该可以正常使用

        顺便提一下,Spring Security 也提供了 AccessDecisionManager 和 AccessDecisionVoters 适配 AuthorizationManager 的方式,用来兼容之前的代码,请点击这里

        至此,Spring Security 的授权架构介绍完毕,在这篇文章中,更多的是方法论,具体的使用细节将在后续文章中进行补充。

猜你喜欢

转载自blog.csdn.net/swadian2008/article/details/131914330
今日推荐