Spring框架通过DelegatingFilterProxy建立Web容器和Spring ApplicationContext的联系。Spring Security基于Filter技术,使用 FilterChainProxy 注册了过滤器链SecurityFilterChain。
Spring Security的过滤器链中会默认添加15个过滤器,其中最后一个过滤器是 FilterSecurityInterceptor,和权限验证关系最为密切。
FilterSecurtiyInterceptor会获取资源访问的授权信息,并根据存储的用户信息,来决定当前请求是否具有访问权限。
FilterSecurityInterceptor
FilterSecurityInterceptor的类关系如下图所示,是抽象类
AbstractSecurityInterceptor的子类,
AbstractSecurityInterceptor 实现了对受保护对象的访问进行拦截的功能。
FilterSecurityInterceptor 使用身份认证管理器 AuthenticationManager 做认证,判断用户是否已登录;使用决策管理器 AccessDecisionManager 做验证,决定用户是否有权限。
AbstractSecurityInterceptor的工作机制可分为以下步骤:
- 查找与当前请求关联的“配置属性”,也就是权限;
- 将安全对象(方法调用或Web请求)、当前身份验证、配置属性提交给决策器(AccessDecisionManager);
- 假设授予了访问权,允许进行安全对象调用;
- 在调用返回之后,如果配置了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