Java高阶—深入学习Spring Security(二)——源码分析Spring Security基本原理

一、概述

  • Spring Security 本质是一个过滤器链FilterSecurityInterceptor是Spring Security 最底层的一个拦截器,是一个方法级的权限过滤器,该类实现了以下方法:
    在这里插入图片描述

  • 接着是一个ExceptionTranslationFilter,是一个异常过滤器,用来处理认证授权过程中抛出的异常。
    在这里插入图片描述

  • 然后是 UsernamePasswordAuthenticationFilter,用于对/loginPOST请求做拦截,校验表单中的用户名和密码。
    在这里插入图片描述

二、源码分析

1、FilterSecurityInterceptor

  • 该类的 doFilter 方法源码如下:
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
        throws IOException, ServletException {
        this.invoke(new FilterInvocation(request, response, chain));
      }
    public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
        if (this.isApplied(filterInvocation) && this.observeOncePerRequest) {
          filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
        } else {
          if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
            filterInvocation.getRequest()
                .setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
          }
          InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
          try {
            filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
          } finally {
            super.finallyInvocation(token);
          }
          super.afterInvocation(token, (Object)null);
        }
      }
    public interface FilterChain {
      void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
    }
    

    2、ExceptionTranslationFilter

    • 该类的 doFilter 方法源码如下:
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
        throws IOException, ServletException {
        this.doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain);
      }
      private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) 
          throws IOException, ServletException {
        try {
          chain.doFilter(request, response);
        } catch (IOException var7) {
          throw var7;
        } catch (Exception var8) {
          Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var8);
          RuntimeException securityException = 
              (AuthenticationException)this.throwableAnalyzer.
              getFirstThrowableOfType(AuthenticationException.class, causeChain);
          if (securityException == null) {
            securityException = (AccessDeniedException)this.throwableAnalyzer.
                getFirstThrowableOfType(AccessDeniedException.class, causeChain);
          }
          if (securityException == null) {
            this.rethrow(var8);
          }
          if (response.isCommitted()) {
            throw new ServletException("Unable to handle the Spring Security Exception 
                                       because the response is already committed.", var8);
          }
          this.handleSpringSecurityException(request, response, chain, 
                                             (RuntimeException)securityException);
        }
      }
      ```
    

3、UsernamePasswordAuthenticationFilter

  • 该类的源码如下:
    public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
      public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
      public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
      private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER 
          = new AntPathRequestMatcher("/login", "POST");
      private String usernameParameter = "username";
      private String passwordParameter = "password";
      private boolean postOnly = true;
      public UsernamePasswordAuthenticationFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
      }
      public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
      }
      public Authentication attemptAuthentication(HttpServletRequest request, 
                                                  HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
          throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
          String username = this.obtainUsername(request);
          username = username != null ? username : "";
          username = username.trim();
          String password = this.obtainPassword(request);
          password = password != null ? password : "";
          UsernamePasswordAuthenticationToken authRequest = 
              new UsernamePasswordAuthenticationToken(username, password);
          this.setDetails(request, authRequest);
          return this.getAuthenticationManager().authenticate(authRequest);
        }
      }
      @Nullable
      protected String obtainPassword(HttpServletRequest request) {
        return request.getParameter(this.passwordParameter);
      }
      @Nullable
      protected String obtainUsername(HttpServletRequest request) {
        return request.getParameter(this.usernameParameter);
      }
      protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
      }
      public void setUsernameParameter(String usernameParameter) {
        Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
        this.usernameParameter = usernameParameter;
      }
      public void setPasswordParameter(String passwordParameter) {
        Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
        this.passwordParameter = passwordParameter;
      }
      public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
      }
      public final String getUsernameParameter() {
        return this.usernameParameter;
      }
      public final String getPasswordParameter() {
        return this.passwordParameter;
      }
    }
    
  • 这个类允许我们去继承和重写attemptAuthentication,如果要重写这个方法,则还必须继承其父类AbstractAuthenticationProcessingFilter并重写successfulAuthentication方法和unsuccessfulAuthentication方法。

三、过滤器链加载过程

  • Spring Security 包含了十多个过滤器,这些过滤器是如何加载到容器中的呢?

1、源码分析

  • 要使用SpringSecurity,首先要对其所用的过滤器进行配置,由于使用了Spring Boot进行自动化配置,因此这一步通常可以省略。

1.1、DelegatingFilterProxy

  • 使用该类进行过滤器链的配置
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
          synchronized(this.delegateMonitor) {
            delegateToUse = this.delegate;
            if (delegateToUse == null) {
              WebApplicationContext wac = this.findWebApplicationContext();
              if (wac == null) {
                throw new 
                    IllegalStateException(
                    "No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?"
                );
              }
              delegateToUse = this.initDelegate(wac);
            }
            this.delegate = delegateToUse;
          }
        }
        this.invokeDelegate(delegateToUse, request, response, filterChain);
      }
    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        String targetBeanName = this.getTargetBeanName();// FilterChainProxy
        Assert.state(targetBeanName != null, "No target bean name set");
        Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);
        if (this.isTargetFilterLifecycle()) {
          delegate.init(this.getFilterConfig());
        }
        return delegate;
      }
    

1.2、FilterChainProxy

  • FilterChainProxy类的 doFilter 使用 doFilterInternal 进行过滤器链的进一步设置:
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
         throws IOException, ServletException {
        boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
        if (!clearContext) {
          this.doFilterInternal(request, response, chain);
        } else {
          try {
            request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
            this.doFilterInternal(request, response, chain);
          } catch (RequestRejectedException var9) {
            this.requestRejectedHandler.handle((HttpServletRequest)request, 
                                               (HttpServletResponse)response, var9);
          } finally {
            SecurityContextHolder.clearContext();
            request.removeAttribute(FILTER_APPLIED);
          }
        }
      }
      private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) 
          throws IOException, ServletException {
        FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
        HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
        List<Filter> filters = this.getFilters((HttpServletRequest)firewallRequest);
        if (filters != null && filters.size() != 0) {
          if (logger.isDebugEnabled()) {
            logger.debug(LogMessage.of(() -> {
              return "Securing " + requestLine(firewallRequest);
            }));
          }
          FilterChainProxy.VirtualFilterChain virtualFilterChain = 
              new FilterChainProxy.VirtualFilterChain(firewallRequest, chain, filters);
          virtualFilterChain.doFilter(firewallRequest, firewallResponse);
        } else {
          if (logger.isTraceEnabled()) {
            logger.trace(LogMessage.of(() -> {
              return "No security for " + requestLine(firewallRequest);
            }));
          }
          firewallRequest.reset();
          chain.doFilter(firewallRequest, firewallResponse);
        }
      }
    

1.3、doFilterInternal的getFilters

 List<Filter> filters = this.getFilters((HttpServletRequest)firewallRequest); 中的 getFilters :
private List<Filter> getFilters(HttpServletRequest request) {
    int count = 0;
    Iterator var3 = this.filterChains.iterator();
    SecurityFilterChain chain;
    do {
      if (!var3.hasNext()) {
        return null;
      }
      chain = (SecurityFilterChain)var3.next();
      if (logger.isTraceEnabled()) {
        ++count;
        logger.trace(LogMessage.format("Trying to match request against %s (%d/%d)", chain, count, 
                                       this.filterChains.size()));
      }
    } while(!chain.matches(request));
    return chain.getFilters();
  }
public interface SecurityFilterChain {
  boolean matches(HttpServletRequest var1);
  List<Filter> getFilters();
}
  • 该方法使用迭代器,将所有需要加载的过滤器加载到容器中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御承扬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值