当前位置:   article > 正文

Spring Security 原理讲解_spring security filter工作原理

spring security filter工作原理

一、整理了解下Spring Security 的工作原理   

  

 

如上图所示,spring security 的主要工作就是在原有的网络请求的过滤器链(ApplicationFilterChain)中额外添加一条过滤器链(FilterChainProxy),主要用于用户认证与授权。

      请求进入web容器经一些容器自身的基础加工后,进入到servlet的滤器链中,spring security 使用 DelegatingFilterProxy 这个filter,将 targetBeanName 设置为 "springSecurityFilterChain" ,也就是security生成的 FilterChainProxy 这个类的bean的名字,使 servlet 过滤器链 指向了 spring security的过滤器链 ,同时也进入到spring 容器当中。经过一系列的认证工作,若认证成功后,会生成一个Authentication类型的对象(可以记录当前登录人的用户信息,权限信息),放入到SecurityContext中(默认以ThreadLocal的方式保存),然后返回servlet 过滤器链中,向控制层前行。

        security的过滤器链会根据你所配置需要启停的功能增减,同时你也可以创建自己的过滤器添加进这个过滤器链中。

 

二、FilterChainProxy源码分析

  1. @Override
  2. public void doFilter(ServletRequest request, ServletResponse response,
  3. FilterChain chain) throws IOException, ServletException {
  4. //此处是对请求是否第一次经过这个过滤器的判断,在spring security中的过滤器中这种用法是相当常见的
  5. boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
  6. if (clearContext) {
  7. try {
  8. request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
  9. doFilterInternal(request, response, chain);
  10. }
  11. finally {
  12. //清除security上下文中的保存的Authentication对象
  13. SecurityContextHolder.clearContext();
  14. request.removeAttribute(FILTER_APPLIED);
  15. }
  16. }
  17. else {
  18. doFilterInternal(request, response, chain);
  19. }
  20. }
  21. private void doFilterInternal(ServletRequest request, ServletResponse response,
  22. FilterChain chain) throws IOException, ServletException {
  23. //对request进行一层包装,同时校验了一下请求的方式是否允许,以及请求地址中是否有非法字符,若不通过抛出异常
  24. FirewalledRequest fwRequest = firewall
  25. .getFirewalledRequest((HttpServletRequest) request);
  26. //对response进行了一层包装,若在response中添加header或cookie时,会对header和cookie的一些属性进行校验
  27. HttpServletResponse fwResponse = firewall
  28. .getFirewalledResponse((HttpServletResponse) response);
  29. //获取spring security中第一个匹配请求地址的过滤器列表
  30. List<Filter> filters = getFilters(fwRequest);
  31. if (filters == null || filters.size() == 0) {
  32. if (logger.isDebugEnabled()) {
  33. logger.debug(UrlUtils.buildRequestUrl(fwRequest)
  34. + (filters == null ? " has no matching filters"
  35. : " has an empty filter list"));
  36. }
  37. fwRequest.reset();
  38. chain.doFilter(fwRequest, fwResponse);
  39. return;
  40. }
  41. //将过滤器列表和servlet过滤器链包装成一个新的过滤器链 VirtualFilterChain 是FilterChainProxy的内部类
  42. VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
  43. vfc.doFilter(fwRequest, fwResponse);
  44. }
  45. private static class VirtualFilterChain implements FilterChain {
  46. private final FilterChain originalChain;
  47. private final List<Filter> additionalFilters;
  48. private final FirewalledRequest firewalledRequest;
  49. private final int size;
  50. private int currentPosition = 0;
  51. private VirtualFilterChain(FirewalledRequest firewalledRequest,
  52. FilterChain chain, List<Filter> additionalFilters) {
  53. this.originalChain = chain;
  54. this.additionalFilters = additionalFilters;
  55. this.size = additionalFilters.size();
  56. this.firewalledRequest = firewalledRequest;
  57. }
  58. @Override
  59. public void doFilter(ServletRequest request, ServletResponse response)
  60. throws IOException, ServletException {
  61. //security 过滤器执行结束后,继续执行servlet过滤器链
  62. if (currentPosition == size) {
  63. if (logger.isDebugEnabled()) {
  64. logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
  65. + " reached end of additional filter chain; proceeding with original chain");
  66. }
  67. // Deactivate path stripping as we exit the security filter chain
  68. this.firewalledRequest.reset();
  69. originalChain.doFilter(request, response);
  70. }
  71. else {
  72. currentPosition++;
  73. Filter nextFilter = additionalFilters.get(currentPosition - 1);
  74. if (logger.isDebugEnabled()) {
  75. logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
  76. + " at position " + currentPosition + " of " + size
  77. + " in additional filter chain; firing Filter: '"
  78. + nextFilter.getClass().getSimpleName() + "'");
  79. }
  80. //传入this,nextFilter中的会继续调用VirtualFilterChain.doFilter()直至所有过滤器执行结束或抛出异常
  81. nextFilter.doFilter(request, response, this);
  82. }
  83. }
  84. }

     FilterChainProxy过滤器通过内部的FilterChain,将请求引入到了security的过滤器链中,上述对其主要的方法进行了简单描述。

 

三、基于SpringBoot,security的加载过程

springboot 启动时 有关spring security 主要加载的类有SecurityAutoConfiguration、SecurityRequestMatcherProviderAutoConfiguration、UserDetailsServiceAutoConfiguration、SecurityFilterAutoConfiguration , 其中需要讲一下SecurityAutoConfiguration 和SecurityFilterAutoConfiguration。

      1、 SecurityAutoConfiguration 引入了SpringBootWebSecurityConfiguration、WebSecurityEnablerConfiguration、SecurityDataConfiguration 这三个配置类:

        SpringBootWebSecurityConfiguration 当你没有主动配置security时,这个类的作用会生效,生一套默认的配置

      WebSecurityEnablerConfiguration 的作用全在@EnableWebSecurity 这个注解上,使Security功能生效,并且承担了其中最主要的初始化工作。

      @EnableWebSecurity 引入了 WebSecurityConfiguration类 ,在这个类中读取了所有的security配置(可以配置多个spring security 过滤器链),并根据这些配置生成一个name为“springSecurityFilterChain” 的bean,里边进行的动作相当多,有兴趣的同学可以研读一下这一块源码。

       SecurityDataConfiguration 属于spring security 的一个扩展配置,此处省略。

     2、SecurityFilterAutoConfiguration 的作用只有一个,就是创建一个DelegatingFilterProxyRegistrationBean,将spring security 的过滤器链springSecurityFilterChain 与servlet过滤器相连。

 

四、注意事项

    1、当手动添加过滤器到spring security中时,如果这个过滤器添加到spring 容器中,spring 自动会将此过滤器添加到servlet过滤器链中,此时,两条过滤器链都会有这个过滤器,则会执行两次。  

        解决办法:

           让这个过滤器继承OncePerRequestFilter ,这个过滤器内部保证只执行一次。

           或者不要将这个过滤器添加到spring容器中。

      2、在使用spring security 自带的csrf防护功能时,在前端并发请求会导致认证不通过,原因是前端同时需要发起多个请求时,这几个请求的发送总是会有一些先后顺序,当第n(n>1)个请求准备发起时,获取到的cookie中的csrfToken为token1, 此时第一个请求的已响应,cookie中的csrfToken随之替换成第一个请求中返回的cookie中的csrfToken2,这时第n个请求发出,header中携带的是token1,cookie中放的是token2,两者不相等,请求拒绝。 所以这个功能要视具体情况而用。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/67704
推荐阅读
相关标签
  

闽ICP备14008679号