Spring Security3源码分析(10)-FilterSecurityInterceptor分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

FilterSecurityInterceptor过滤器对应的类路径为

org.springframework.security.web.access.intercept.FilterSecurityInterceptor

这个filter是filterchain中比较复杂,也是比较核心的过滤器,主要负责授权的工作

在看这个filter源码之前,先来看看spring是如何构造filter这个bean的

具体的构造过程的代码片段为

Java代码

  1. //这个方法源自HttpConfigurationBuilder类
  2. void createFilterSecurityInterceptor(BeanReference authManager) {
  3. //判断是否配置了use-expressions属性
  4. boolean useExpressions = FilterInvocationSecurityMetadataSourceParser.isUseExpressions(httpElt);
  5. //根据intercept-url标签列表创建授权需要的元数据信息。后面仔细分析
  6. BeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser.createSecurityMetadataSource(interceptUrls, httpElt, pc);
  7. RootBeanDefinition accessDecisionMgr;
  8. //创建voter列表
  9. ManagedList voters = new ManagedList(2);
  10. //如果是使用了表达式,使用WebExpressionVoter
  11. //没使用表达式,就使用RoleVoter、AuthenticatedVoter
  12. if (useExpressions) {
  13. voters.add(new RootBeanDefinition(WebExpressionVoter.class));
  14. } else {
  15. voters.add(new RootBeanDefinition(RoleVoter.class));
  16. voters.add(new RootBeanDefinition(AuthenticatedVoter.class));
  17. }
  18. //定义授权的决策管理类AffirmativeBased
  19. accessDecisionMgr = new RootBeanDefinition(AffirmativeBased.class);
  20. //添加依赖的voter列表
  21. accessDecisionMgr.getPropertyValues().addPropertyValue(“decisionVoters”, voters);
  22. accessDecisionMgr.setSource(pc.extractSource(httpElt));
  23. // Set up the access manager reference for http
  24. String accessManagerId = httpElt.getAttribute(ATT_ACCESS_MGR);
  25. //如果未定义access-decision-manager-ref属性,就使用默认的
  26. //AffirmativeBased
  27. if (!StringUtils.hasText(accessManagerId)) {
  28. accessManagerId = pc.getReaderContext().generateBeanName(accessDecisionMgr);
  29. pc.registerBeanComponent(new BeanComponentDefinition(accessDecisionMgr, accessManagerId));
  30. }
  31. //创建FilterSecurityInterceptor过滤器
  32. BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
  33. //添加决策管理器
  34. builder.addPropertyReference(“accessDecisionManager”, accessManagerId);
  35. //添加认证管理类
  36. builder.addPropertyValue(“authenticationManager”, authManager);
  37. if (“false”.equals(httpElt.getAttribute(ATT_ONCE_PER_REQUEST))) {
  38. builder.addPropertyValue(“observeOncePerRequest”, Boolean.FALSE);
  39. }
  40. //添加授权需要的安全元数据资源
  41. builder.addPropertyValue(“securityMetadataSource”, securityMds);
  42. BeanDefinition fsiBean = builder.getBeanDefinition();
  43. //向ioc容器注册bean
  44. String fsiId = pc.getReaderContext().generateBeanName(fsiBean);
  45. pc.registerBeanComponent(new BeanComponentDefinition(fsiBean,fsiId));
  46. // Create and register a DefaultWebInvocationPrivilegeEvaluator for use with taglibs etc.
  47. BeanDefinition wipe = new RootBeanDefinition(DefaultWebInvocationPrivilegeEvaluator.class);
  48. wipe.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(fsiId));
  49. pc.registerBeanComponent(new BeanComponentDefinition(wipe, pc.getReaderContext().generateBeanName(wipe)));
  50. this.fsi = new RuntimeBeanReference(fsiId);
  51. }

现在再仔细分析创建元数据资源的bean过程

Java代码

  1. static BeanDefinition createSecurityMetadataSource(List interceptUrls, Element elt, ParserContext pc) {
  2. //创建Url处理类,有两个实现:AntUrlPathMatcher、RegexUrlPathMatcher
  3. UrlMatcher matcher = HttpSecurityBeanDefinitionParser.createUrlMatcher(elt);
  4. boolean useExpressions = isUseExpressions(elt);
  5. //解析intercept-url标签,构造所有需要拦截url的map信息
  6. //map中的key:RequestKey的bean定义,value:SecurityConfig的bean定义
  7. ManagedMap<BeanDefinition, BeanDefinition> requestToAttributesMap = parseInterceptUrlsForFilterInvocationRequestMap(
  8. interceptUrls, useExpressions, pc);
  9. BeanDefinitionBuilder fidsBuilder;
  10. if (useExpressions) {
  11. //定义表达式处理类的bean
  12. Element expressionHandlerElt = DomUtils.getChildElementByTagName(elt, Elements.EXPRESSION_HANDLER);
  13. String expressionHandlerRef = expressionHandlerElt == null ? null : expressionHandlerElt.getAttribute(“ref”);
  14. if (StringUtils.hasText(expressionHandlerRef)) {
  15. logger.info(“Using bean ‘” + expressionHandlerRef + “‘ as web SecurityExpressionHandler implementation”);
  16. } else {
  17. BeanDefinition expressionHandler = BeanDefinitionBuilder.rootBeanDefinition(DefaultWebSecurityExpressionHandler.class).getBeanDefinition();
  18. expressionHandlerRef = pc.getReaderContext().generateBeanName(expressionHandler);
  19. pc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));
  20. }
  21. //定义表达式类型的FilterInvocationSecurityMetadataSource
  22. fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(ExpressionBasedFilterInvocationSecurityMetadataSource.class);
  23. //通过构造函数注入依赖
  24. fidsBuilder.addConstructorArgValue(matcher);
  25. fidsBuilder.addConstructorArgValue(requestToAttributesMap);
  26. fidsBuilder.addConstructorArgReference(expressionHandlerRef);
  27. } else {
  28. //定义非表达式类型的FilterInvocationSecurityMetadataSource
  29. fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);
  30. //通过构造函数注入依赖
  31. fidsBuilder.addConstructorArgValue(matcher);
  32. fidsBuilder.addConstructorArgValue(requestToAttributesMap);
  33. }
  34. fidsBuilder.addPropertyValue(“stripQueryStringFromUrls”, matcher instanceof AntUrlPathMatcher);
  35. fidsBuilder.getRawBeanDefinition().setSource(pc.extractSource(elt));
  36. return fidsBuilder.getBeanDefinition();
  37. }

通过以上的bean构造过程,FilterSecurityInterceptor所依赖的决策管理器、认证管理器、安全元数据资源都具备了,该让FilterSecurityInterceptor干活了,其源码为

Java代码

  1. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  2. throws IOException, ServletException {
  3. //封装request, response, chain,方便参数传递、增加代码阅读性
  4. FilterInvocation fi = new FilterInvocation(request, response, chain);
  5. invoke(fi);
  6. }
  7. public void invoke(FilterInvocation fi) throws IOException, ServletException {
  8. if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
  9. && observeOncePerRequest) {
  10. if (fi.getRequest() != null) {
  11. fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
  12. }
  13. //执行父类beforeInvocation,类似于aop中的before
  14. InterceptorStatusToken token = super.beforeInvocation(fi);
  15. try {
  16. //filter传递
  17. fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
  18. } finally {
  19. //执行父类的afterInvocation,类似于aop中的after
  20. super.afterInvocation(token, null);
  21. }
  22. }
  23. }

继续看父类的beforeInvocation方法,其中省略了一些不重要的代码片段

Java代码

  1. protected InterceptorStatusToken beforeInvocation(Object object) {
  2. //根据SecurityMetadataSource获取配置的权限属性
  3. Collection attributes = this.obtainSecurityMetadataSource().getAttributes(object);
  4. //省略……
  5. //判断是否需要对认证实体重新认证,默认为否
  6. Authentication authenticated = authenticateIfRequired();
  7. // Attempt authorization
  8. try {
  9. //决策管理器开始决定是否授权,如果授权失败,直接抛出AccessDeniedException
  10. this.accessDecisionManager.decide(authenticated, object, attributes);
  11. }
  12. catch (AccessDeniedException accessDeniedException) {
  13. publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
  14. accessDeniedException));
  15. throw accessDeniedException;
  16. }
  17. }

增加说明

Java代码

  1. Collection attributes = this.obtainSecurityMetadataSource().getAttributes(object);

这里获取的是权限列表信息,比如说有这个配置

如果现在发起一个请求时index.jsp,那么根据这个请求返回的attributes集合就是分别包含ROLE\_USER,ROLE\_ADMIN属性的两个SecurityConfig对象 至于请求url如何匹配的,大家可以通过阅读DefaultFilterInvocationSecurityMetadataSource类的源码,实际上,这里用到了spring的路径匹配工具类org.springframework.util.AntPathMatcher AntPathMatcher匹配方式的通配符有三种: ?(匹配任何单字符),\*(匹配0或者任意数量的字符),\*\*(匹配0或者更多的目录) 由于之前在bean的定义过程已经知道决策管理器是AffirmativeBased,接着看AffirmativeBased的决策过程 Java代码 1. **public** **void** decide(Authentication authentication, Object object, Collection configAttributes) 2. **throws** AccessDeniedException \{ 3. **int** deny = 0; 4. //循环voters,实际上是RoleVoter、AuthenticatedVoter 5. **for** (AccessDecisionVoter voter : getDecisionVoters()) \{ 6. //把具体的决策任务交给voter处理 7. //voter只返回-1、0、1,只有为1才算授权成功 8. **int** result = voter.vote(authentication, object, configAttributes); 9. 10. **if** (logger.isDebugEnabled()) \{ 11. logger.debug(“Voter: “ \+ voter + “, returned: “ \+ result); 12. \} 13. 14. **switch** (result) \{ 15. **case** AccessDecisionVoter.ACCESS\_GRANTED: 16. **return**; 17. 18. **case** AccessDecisionVoter.ACCESS\_DENIED: 19. deny++; 20. 21. **break**; 22. 23. **default**: 24. **break**; 25. \} 26. \} 27. //只要有一个voter拒绝了,则直接抛出访问拒绝异常 28. **if** (deny > 0) \{ 29. **throw** **new** AccessDeniedException(messages.getMessage(“AbstractAccessDecisionManager.accessDenied”, 30. “Access is denied”)); 31. \} 32. 33. // To get this far, every AccessDecisionVoter abstained 34. checkAllowIfAllAbstainDecisions(); 35. \} 实际上,有三种决策管理器,分别为AffirmativeBased、ConsensusBased、UnanimousBased,各自决策的区别是: AffirmativeBased:只要有一个voter投同意票,就授权成功 ConsensusBased:只要投同意票的大于投反对票的,就授权成功 UnanimousBased:需要一致通过才授权成功 具体决策规则很简单,只是根据voter返回的结果做处理 接下来,分别看RoleVoter、AuthenticatedVoter的源码 RoleVoter: Java代码 1. **public** **int** vote(Authentication authentication, Object object, Collection attributes) \{ 2. **int** result = ACCESS\_ABSTAIN; 3. //从认证实体中获取所有的权限列表 4. Collection authorities = extractAuthorities(authentication); 5. //循环intercept-url配置的access权限列表 6. **for** (ConfigAttribute attribute : attributes) \{ 7. **if** (**this**.supports(attribute)) \{ 8. result = ACCESS\_DENIED; 9. 10. // Attempt to find a matching granted authority 11. //循环认证实体所拥有的权限列表 12. **for** (GrantedAuthority authority : authorities) \{ 13. **if** (attribute.getAttribute().equals(authority.getAuthority())) \{ 14. //只要有相同的权限,直接返回成功1 15. **return** ACCESS\_GRANTED; 16. \} 17. \} 18. \} 19. \} 20. 21. **return** result; 22. \} AuthenticatedVoter: Java代码 1. **public** **int** vote(Authentication authentication, Object object, Collection attributes) \{ 2. **int** result = ACCESS\_ABSTAIN; 3. 4. **for** (ConfigAttribute attribute : attributes) \{ 5. **if** (**this**.supports(attribute)) \{ 6. result = ACCESS\_DENIED; 7. 8. **if** (IS\_AUTHENTICATED\_FULLY.equals(attribute.getAttribute())) \{ 9. **if** (isFullyAuthenticated(authentication)) \{ 10. **return** ACCESS\_GRANTED; 11. \} 12. \} 13. 14. **if** (IS\_AUTHENTICATED\_REMEMBERED.equals(attribute.getAttribute())) \{ 15. **if** (authenticationTrustResolver.isRememberMe(authentication) 16. || isFullyAuthenticated(authentication)) \{ 17. **return** ACCESS\_GRANTED; 18. \} 19. \} 20. 21. **if** (IS\_AUTHENTICATED\_ANONYMOUSLY.equals(attribute.getAttribute())) \{ 22. **if** (authenticationTrustResolver.isAnonymous(authentication) || isFullyAuthenticated(authentication) 23. || authenticationTrustResolver.isRememberMe(authentication)) \{ 24. **return** ACCESS\_GRANTED; 25. \} 26. \} 27. \} 28. \} 29. 30. **return** result; 31. \} 由于RoleVoter在list列表中的位置处于AuthenticatedVoter前面,只要RoleVoter通过,就不会再执行AuthenticatedVoter了。实际上AuthenticatedVoter只会对IS\_AUTHENTICATED\_FULLY、IS\_AUTHENTICATED\_REMEMBERED、IS\_AUTHENTICATED\_ANONYMOUSLY三种权限做vote处理。 ------- 来源:[http://ddrv.cn](http://ddrv.cn)
赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring Security3源码分析(10)-FilterSecurityInterceptor分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏