Spring Security's request processing flow

Preface

Analyzing the core principles of Spring Security can be carried out from the following aspects:

  1. What does Spring Security do when the system starts?
  2. What does Spring Security do after making a request?

System startup

What does SpringSecurity do when our web service starts? When the system starts, the configured web.xml file will be loaded first.

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app version="2.5" id="WebApp_ID" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name>Archetype Created Web Application</display-name>

  <!-- 初始化spring容器 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- post乱码过滤器 -->
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!-- 前端控制器 -->
  <servlet>
    <servlet-name>dispatcherServletb</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServletb</servlet-name>
    <!-- 拦截所有请求jsp除外 -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!-- 配置过滤器链 springSecurityFilterChain 名称固定 -->
  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>

Configuration information related to SpringSecurity in web.xml:

  1. Spring's initialization (the configuration file that parses SpringSecurity will be loaded)
  2. Load DelegatingFilterProxy filter

Spring's initialization operation will load the SpringSecurity configuration file and add relevant data to the Spring container.

image.png

DelegatingFilterProxy filter : This filter itself has nothing to do with SpringSecurity and will intercept all requests, including SpringSecurity-related filters.

Spring Security's handling of requests

A request sent by a client will be intercepted by many Web Filters. The DelegatingFilterProxy filter defined in web.xml will intercept all requests from the client.

image.png

When a user request comes in, it will be intercepted by the doFilter method.

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
    
    Filter delegateToUse = this.delegate;
    if (delegateToUse == null) {
    
    
        // 如果 delegateToUse 为空 那么完成init中的初始化操作
        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);
}

invokeDelegate

protected void invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
    
    // delegate.doFilter() FilterChainProxy
    delegate.doFilter(request, response, filterChain);
}

Here you can find that DelegatingFilterProxy is ultimately the doFilter method of the delegated proxy object called

FilterChainProxy

Proxy object of the filter chain : Enhanced filter chain (the specific filter that processes requests is not FilterChainProxy) Matches the appropriate filter chain to process the request according to the client's request, as follows

image.png

// 处理用户请求
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
    
    boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
    if (clearContext) {
    
    
        try {
    
    
            request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
            this.doFilterInternal(request, response, chain);
        } finally {
    
    
            SecurityContextHolder.clearContext();
            request.removeAttribute(FILTER_APPLIED);
        }
    } else {
    
    
        this.doFilterInternal(request, response, chain);
    }

}

doFilterInternal gets the filter chain corresponding to the processing request, and then processes it one by one.

image.png

CSRFFilter : is a filter used in Spring Security to prevent cross-site request forgery (CSRF) attacks. CSRFFilter prevents CSRF attacks by verifying whether the source of the request and the authentication information match before the request reaches the target resource. It checks whether the request meets the following conditions before processing it:

  • Whether the request comes from an authenticated user.
  • Whether the request matches the authentication information of the target resource.

If both of the above conditions are met, the request is allowed to continue processing, otherwise it will be rejected or redirected to the login page.

UsernamePasswordAuthenticationFilter : It is the filter used by Spring Security to verify the login user password. It is described in detail in the authentication process .

DefaultLoginPageGeneratingFilter : is the filter responsible for generating login pages in Spring Security. When the developer does not configure a login page in the security configuration, Spring Security Web will automatically construct a login page for the user.

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    
    
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;
    boolean loginError = this.isErrorPage(request);
    boolean logoutSuccess = this.isLogoutSuccess(request);
    if (!this.isLoginUrlRequest(request) && !loginError && !logoutSuccess) {
    
    
        // 正常的业务请求就直接放过
        chain.doFilter(request, response);
    } else {
    
    
        // 需要跳转到登录页面的请求
        String loginPageHtml = this.generateLoginPageHtml(request, loginError, logoutSuccess);
        // 直接响应登录页面
        response.setContentType("text/html;charset=UTF-8");
        response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);
        response.getWriter().write(loginPageHtml);
    }
}

generateLoginPageHtml

private String generateLoginPageHtml(HttpServletRequest request, boolean loginError, boolean logoutSuccess) {
    
    
    String errorMsg = "Invalid credentials";
    if (loginError) {
    
    
        HttpSession session = request.getSession(false);
        if (session != null) {
    
    
            AuthenticationException ex = (AuthenticationException)session.getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
            errorMsg = ex != null ? ex.getMessage() : "Invalid credentials";
        }
    }

    StringBuilder sb = new StringBuilder();
    sb.append("<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n    <meta name=\"description\" content=\"\">\n    <meta name=\"author\" content=\"\">\n    <title>Please sign in</title>\n    <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n    <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n  </head>\n  <body>\n     <div class=\"container\">\n");
    String contextPath = request.getContextPath();
    if (this.formLoginEnabled) {
    
    
        sb.append("      <form class=\"form-signin\" method=\"post\" action=\"" + contextPath + this.authenticationUrl + "\">\n        <h2 class=\"form-signin-heading\">Please sign in</h2>\n" + createError(loginError, errorMsg) + createLogoutSuccess(logoutSuccess) + "        <p>\n          <label for=\"username\" class=\"sr-only\">Username</label>\n          <input type=\"text\" id=\"username\" name=\"" + this.usernameParameter + "\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n        </p>\n        <p>\n          <label for=\"password\" class=\"sr-only\">Password</label>\n          <input type=\"password\" id=\"password\" name=\"" + this.passwordParameter + "\" class=\"form-control\" placeholder=\"Password\" required>\n        </p>\n" + this.createRememberMe(this.rememberMeParameter) + this.renderHiddenInputs(request) + "        <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n      </form>\n");
    }

    if (this.openIdEnabled) {
    
    
        sb.append("      <form name=\"oidf\" class=\"form-signin\" method=\"post\" action=\"" + contextPath + this.openIDauthenticationUrl + "\">\n        <h2 class=\"form-signin-heading\">Login with OpenID Identity</h2>\n" + createError(loginError, errorMsg) + createLogoutSuccess(logoutSuccess) + "        <p>\n          <label for=\"username\" class=\"sr-only\">Identity</label>\n          <input type=\"text\" id=\"username\" name=\"" + this.openIDusernameParameter + "\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n        </p>\n" + this.createRememberMe(this.openIDrememberMeParameter) + this.renderHiddenInputs(request) + "        <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n      </form>\n");
    }

    if (this.oauth2LoginEnabled) {
    
    
        sb.append("<h2 class=\"form-signin-heading\">Login with OAuth 2.0</h2>");
        sb.append(createError(loginError, errorMsg));
        sb.append(createLogoutSuccess(logoutSuccess));
        sb.append("<table class=\"table table-striped\">\n");
        Iterator var7 = this.oauth2AuthenticationUrlToClientName.entrySet().iterator();

        while(var7.hasNext()) {
    
    
            Entry<String, String> clientAuthenticationUrlToClientName = (Entry)var7.next();
            sb.append(" <tr><td>");
            String url = (String)clientAuthenticationUrlToClientName.getKey();
            sb.append("<a href=\"").append(contextPath).append(url).append("\">");
            String clientName = HtmlUtils.htmlEscape((String)clientAuthenticationUrlToClientName.getValue());
            sb.append(clientName);
            sb.append("</a>");
            sb.append("</td></tr>\n");
        }

        sb.append("</table>\n");
    }

    sb.append("</div>\n");
    sb.append("</body></html>");
    return sb.toString();
}

ExceptionTranslationFilter : It is the second to last in the filter chain. If the request is made when not logged in, this filter will redirect to the configured login page.

Insert image description here

Summarize

Through the above analysis, we can know that Spring Security uses a series of filters to provide its security functions. Including CsrfFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, ExceptionTranslationFilter, etc., they play their respective roles in Spring Security and work together to provide complete security.

Guess you like

Origin blog.csdn.net/qq_28314431/article/details/132982827