基于Spring Security框架实现资源访问的权限控制

Spring框架通过DelegatingFilterProxy建立Web容器和Spring ApplicationContext的联系。Spring Security基于Filter技术,使用 FilterChainProxy 注册了过滤器链SecurityFilterChain。

再谈基于Spring Security框架实现资源访问的权限控制

Spring Security可以认为就是一个Filter,同时也是一个FilterChain

Spring Security的过滤器链中会默认添加15个过滤器,其中最后一个过滤器是 FilterSecurityInterceptor,和权限验证关系最为密切。

再谈基于Spring Security框架实现资源访问的权限控制

Spring Security基本原理

FilterSecurtiyInterceptor会获取资源访问的授权信息,并根据存储的用户信息,来决定当前请求是否具有访问权限。

FilterSecurityInterceptor

FilterSecurityInterceptor的类关系如下图所示,是抽象类
AbstractSecurityInterceptor的子类,
AbstractSecurityInterceptor 实现了对受保护对象的访问进行拦截的功能。

再谈基于Spring Security框架实现资源访问的权限控制

FilterSecurityInterceptor类关系

FilterSecurityInterceptor 使用身份认证管理器 AuthenticationManager 做认证,判断用户是否已登录;使用决策管理器 AccessDecisionManager 做验证,决定用户是否有权限。


AbstractSecurityInterceptor
的工作机制可分为以下步骤:

  1. 查找与当前请求关联的“配置属性”,也就是权限;
  2. 将安全对象(方法调用或Web请求)、当前身份验证、配置属性提交给决策器(AccessDecisionManager);
  3. 假设授予了访问权,允许进行安全对象调用;
  4. 在调用返回之后,如果配置了AfterInvocationManager。如果调用引发异常,则不会调用AfterInvocationManager。

除了使用默认的FilterSecurityInterceptor,我们也可以自定义
AbstractSecurityInterceptor实现类,下面展示一个自定义AbstractSecurityInterceptor的示例。

自定义AbstractSecurityInterceptor实现类

MyFilterSecurityInterceptor 是一个自定义
AbstractSecurityInterceptor 实现类,同时还实现了Filter接口。

public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    @Autowired
    private FilterInvocationSecurityMetadataSource securityMetadataSource;
    
    @Autowired
    public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
        super.setAccessDecisionManager(myAccessDecisionManager);
    }
  
    public void invoke(FilterInvocation fi) throws IOException, ServletException {
		    InterceptorStatusToken token = super.beforeInvocation(fi);
		    try {
			      fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		    } finally {
			     super.afterInvocation(token, null);
		    }
	  }
}

MyFilterSecurityInterceptor类中包含了两个成员:
FilterInvocationSecurityMetadataSource和 MyAccessDecisionManager。前者继承自SecurityMetadataSource接口,在其实现类中需要初始化资源对应的角色;后者实现了 AccessDecisionManager 接口,最终决定当前请求是否可以被放行。

参数 FilterInvocation 实例中包含被拦截的url,调用 SecurityMetadataSource 接口的 getAttributes() 方法可以获取访问该url所对应的权限,再调用 AccessDecisionManager 接口的 decide() 方法来校验用户的权限是否足够。

MyInvocationSecurityMetadataSourceService

自定义
MyInvocationSecurityMetadataSource 类实现了
FilterInvocationSecurityMetadataSource接口,它的作用就是用来储存请求与权限的对应关系。

@Component
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
   
    @Autowired
    private PermissionMapper permissionMapper;

    /**
     * 每一个资源所需要的角色 Collection<ConfigAttribute>决策器会用到
     */
    private static HashMap<String, Collection<ConfigAttribute>> map =null;

    /**
     * 返回请求的资源需要的角色
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
        HttpServletRequest request = ((FilterInvocation) o).getHttpRequest();
        for (Iterator<String> it = map.keySet().iterator(); it.hasNext();) {
            String url = it.next();
            if (new AntPathRequestMatcher( url ).matches( request )) {
                return map.get( url );
            }
        }
        return null;
    }

    /**
    * 初始化所有资源对应的角色
    */
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {      
        ... ...
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

SecurityMetadataSource 接口有3个方法:

  • boolean supports(Class<?> clazz):指示该类是否能够为指定的方法调用或Web请求提供ConfigAttributes。
  • Collection<ConfigAttribute> getAllConfigAttributes():Spring容器启动时自动调用, 一般把所有请求与权限的对应关系也要在这个方法里初始化,保存在一个属性变量里。
  • Collection<ConfigAttribute> getAttributes(Object object):当接收到一个http请求时, FilterSecurityInterceptor会调用这个方法,其中参数object是一个包含url信息的HttpServletRequest实例。这个方法要返回请求该url所需要的权限集合。

有了请求对应的权限,接下来就是由AccessDecisionManager接口的实现类来进行权限认证,决定是否允许请求访问资源。

自定义决策类 MyAccessDecisionManager

@Component
public class MyAccessDecisionManager implements AccessDecisionManager {
    /**
     * 通过传递的参数来决定用户是否有访问对应受保护对象的权限
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
        if (null == configAttributes || 0 >= configAttributes.size()) {
            return;
        } else {
            String needRole;
            for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
                needRole = iter.next().getAttribute();
                for(GrantedAuthority ga : authentication.getAuthorities()) {
                    if(needRole.trim().equals(ga.getAuthority().trim())) {
                        return;
                    }
                }
            }
            throw new AccessDeniedException("当前访问没有权限");
        }
    }

    /**
     * 表示此AccessDecisionManager是否能够处理参数ConfigAttribute的授权请求
     */
    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    /**
     * 表示当前AccessDecisionManager实现是否能够为指定的安全对象(方法调用或Web请求)提供访问控制决策
     */
    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

}

decide() 方法是判定是否拥有权限的决策方法,包括三个参数:

  • Authentication authentication :UserService中循环添加到 GrantedAuthority 对象中的权限信息集合.
  • Object object 包含了客户端请求的requset信息,可转换为
HttpServletRequest request = ((FilterInvocation) object).getHttpRequest()
  • Collection<ConfigAttribute> configAttributes 为 MyInvocationSecurityMetadataSource的getAttributes() 方法返回的结果。

总结

基于Spring Security框架实现资源访问的权限控制,我们要重点关注三个接口:

  • AbstractSecurityInterceptor接口:实现权限认证的整体流程。
  • SecurityMetadataSource接口:决定请求url所需要的的权限。
  • AccessDecisionManager接口:根据url所需权限以及用户角色,判断是否允许请求访问资源。

内容出处:,

声明:本网站所收集的部分公开资料来源于互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。如果您发现网站上有侵犯您的知识产权的作品,请与我们取得联系,我们会及时修改或删除。文章链接:http://www.yixao.com/tech/28146.html

发表评论

登录后才能评论