spring security 简单分析
1:spring security 对于普通的URL权限验证基于过滤器实现的
在web.xml 文件中这样配置
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter>
其中 filter-name一定要配成springSecurityFilterChain,因为DelegatingFilterProxy初始化的时候回根据配置的filter-name 从容器中查找对因的spring security 统一处理过滤器 返回的是FilterChainProxy 。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain); //根据请求的URL 放回相应的过滤器 List filters = getFilters(fi.getRequestUrl()); if (filters == null || filters.size() == 0) { if (logger.isDebugEnabled()) { logger.debug(fi.getRequestUrl() + filters == null ? " has no matching filters" : " has an empty filter list"); } chain.doFilter(request, response); return; } //构建spring security 过滤器链对象 VirtualFilterChain virtualFilterChain = new VirtualFilterChain(fi, filters); virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse()); }
根据Url返回过滤器其中会包含一个FilterSecurityInterceptor过滤器,这个是专门处理http url 权限认证的过滤器,下面是他的doFilter
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain); invoke(fi); } public void invoke(FilterInvocation fi) throws IOException, ServletException { if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null) && observeOncePerRequest) { // filter already applied to this request and user wants us to observce // once-per-request handling, so don't re-do security checking fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } else { // first time this request being called, so perform security checking if (fi.getRequest() != null) { fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE); } //这个方法是权限验证的入口调用父类的beforeInvocation方法 InterceptorStatusToken token = super.beforeInvocation(fi); try { fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.afterInvocation(token, null); } } }
在上面我们看到invoke 方法会调用父类的beforeInvocation方法。父类是AbstractSecurityInterceptor,这个类是权限验证的主类,
包含子类有
- EndpointInterceptor
- FilterSecurityInterceptor 对应http资源过滤
- MethodSecurityInterceptor 对应spring aop 拦截过滤
- AspectJSecurityInterceptor
- AspectJAnnotationSecurityInterceptor
protected InterceptorStatusToken beforeInvocation(Object object) { Assert.notNull(object, "Object was null"); if (!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: " + getSecureObjectClass()); } //获取资源对应的权限配置 ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object); if (attr == null) { if (rejectPublicInvocations) { throw new IllegalArgumentException( "No public invocations are allowed via this AbstractSecurityInterceptor. " + "This indicates a configuration error because the " + "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'"); } if (logger.isDebugEnabled()) { logger.debug("Public object - authentication not attempted"); } publishEvent(new PublicInvocationEvent(object)); return null; // no further work post-invocation } if (logger.isDebugEnabled()) { logger.debug("Secure object: " + object + "; ConfigAttributes: " + attr); } if (SecurityContextHolder.getContext().getAuthentication() == null) { credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attr); } //判断每次请求是否都需要进行认证检查 Authentication authenticated = authenticateIfRequired(); try { //权限验证 this.accessDecisionManager.decide(authenticated, object, attr); } catch (AccessDeniedException accessDeniedException) { AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated, accessDeniedException); publishEvent(event); throw accessDeniedException; } if (logger.isDebugEnabled()) { logger.debug("Authorization successful"); } AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated); publishEvent(event); // Attempt to run as a different user Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr); if (runAs == null) { if (logger.isDebugEnabled()) { logger.debug("RunAsManager did not change Authentication object"); } // no further work post-invocation return new InterceptorStatusToken(authenticated, false, attr, object); } else { if (logger.isDebugEnabled()) { logger.debug("Switching to RunAs Authentication: " + runAs); } SecurityContextHolder.getContext().setAuthentication(runAs); // revert to token.Authenticated post-invocation return new InterceptorStatusToken(authenticated, true, attr, object); } }
在上面方法中 this.accessDecisionManager.decide(authenticated, object, attr);才是资源授权的 地方,accessDecisionManager是授权访问管理器,默认实现由三种
(1)UnanimousBased(全票通过):所有投票器都通过才允许访问资源。
(2)ConsensusBased(少数服从多数):超过一半的投票器通过才允许访问资源。
(3)AffirmativeBased(一票通过):只要有一个投票器投票通过,就允许访问资源。
URL资源授权默认走AffirmativeBased这个授权访问管理器,其授权代码如下
public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config) throws AccessDeniedException { //获取当前访问授权管理器的投票器 Iterator iter = this.getDecisionVoters().iterator(); int deny = 0; while (iter.hasNext()) { AccessDecisionVoter voter = (AccessDecisionVoter) iter.next(); int result = voter.vote(authentication, object, config); switch (result) { case AccessDecisionVoter.ACCESS_GRANTED: return; case AccessDecisionVoter.ACCESS_DENIED: deny++; break; default: break; } } if (deny > 0) { throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied")); } // To get this far, every AccessDecisionVoter abstained checkAllowIfAllAbstainDecisions(); }
Spring Security中有很多AccessDecisionVoter的实现,最简单的有RoleVoter。RoleVoter通过将Authentication里面获取的权限信息,与从ConfigAttributeDefinition配置的访问资源需要的权限对比,来投票通过,或拒绝,或弃权。代码如下
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) { int result = ACCESS_ABSTAIN; Iterator iter = config.getConfigAttributes().iterator(); GrantedAuthority[] authorities = extractAuthorities(authentication); while (iter.hasNext()) { ConfigAttribute attribute = (ConfigAttribute) iter.next(); if (this.supports(attribute)) { result = ACCESS_DENIED; // Attempt to find a matching granted authority for (int i = 0; i < authorities.length; i++) { if (attribute.getAttribute().equals(authorities[i].getAuthority())) { return ACCESS_GRANTED; } } } } return result; }
相关推荐
yupi0 2020-10-10
spring 2020-08-18
编程点滴 2020-07-29
幸运小侯子 2020-07-05
itjavashuai 2020-07-04
qingjiuquan 2020-06-29
shushan 2020-06-25
小鱿鱼 2020-06-22
咻pur慢 2020-06-18
melonjj 2020-06-17
qingjiuquan 2020-06-13
neweastsun 2020-06-05
小鱿鱼 2020-06-05
mxcsdn 2020-05-31
吾日五省我身 2020-05-27
牧场SZShepherd 2020-05-27
sweetgirl0 2020-05-14