SpringSecurity 依据用户请求的过程进行源码解析

SpringSecurity实现安全管理主要通过滤器(filter)、验证器(AuthenticationManager)、用户数据提供器(ProviderManager)、授权器(accessDecisionManager)、投票器(AccessDecisionVoter)这几个基本模块协作完成的。大概分为两个部分 用户验证授权 这个两个部分。这个部分主要在AuthenticationProcessingFilter和AbstractSecurityInterceptor中完成。

使用过SpringSecurity的用户应该知道,首先应该知道web.xml中申明如下配置

<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>

大家不要误认为DelegatingFilterProxy是springsecurity的入口,其实DelegatingFilterProxy其实这个类位于spring-web-3.0.5.RELEASE.jar就说明 这个类本身与springsecurity

无关。其实这个类的作用就是就是拦截请求,把这个请求过滤给springSecurityFilterChain的对应的类(FilterChainProxy)来处理。我们通过断点可以发现,当发送请求时首先进入这个DelegatingFilterProxy这个doFilter进行请求拦截,相关的源码如下:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

// Lazily initialize the delegate if necessary.
          Filter delegateToUse = this.delegate;
         if (delegateToUse == null) {
         synchronized (this.delegateMonitor) {
           if (this.delegate == null) {
          WebApplicationContext wac = findWebApplicationContext();
          if (wac == null) {
        throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
      }
      this.delegate = initDelegate(wac);
   }
      delegateToUse = this.delegate;
  } 
}

// Let the delegate perform the actual doFilter operation.
      invokeDelegate(delegateToUse, request, response, filterChain);
}

这里的核心代码就是invokeDelegate(delegateToUse, request, response, filterChain);这个方法,查看的invokeDelegate方法的源码

protected void invokeDelegate(
          Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
       throws ServletException, IOException {

delegate.doFilter(request, response, filterChain);
}

可以看出就是把这个请求委托给FilterChainProxy来处理,delegate通过断点可以看出就是FilterChainProxy,这个过滤器链默认的顺序为

ChannelProcessingFilter
       SecurityContextPersistenceFilter
       ConcurrentSessionFilter
       LogoutFilter
       UsernamePasswordAuthenticationFilter/CasAuthenticationFilter/BasicAuthenticationFilter 
       SecurityContextHolderAwareRequestFilter
       JaasApiIntegrationFilter
       RememberMeAuthenticationFilter
       AnonymousAuthenticationFilter
       ExceptionTranslationFilter
       FilterSecurityInterceptor

其中加粗的为重点,UsernamePasswordAuthenticationFilter等(进行登陆验证),FilterSecurityInterceptor(进行授权管理),这两个过滤器一般要自定义。

进入到FilterChainProxy 的 doFilter 相关源码如下:

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

//判断是否进行过滤申请了
            boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
            if (clearContext) {
            try {

//给FILTER_APPLIED 给设置为true,做一个申请的标志
             request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

//这个方法是重点 相关的源码如下:
                doFilterInternal(request, response, chain);
              }
            finally {
             SecurityContextHolder.clearContext();
              request.removeAttribute(FILTER_APPLIED);
            }
        }
else {
doFilterInternal(request, response, chain);
}
}

doFilterInternal 的的源码解析:

private void doFilterInternal(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

FirewalledRequest fwRequest = firewall
                             .getFirewalledRequest((HttpServletRequest) request);
                             HttpServletResponse fwResponse = firewall
                            .getFirewalledResponse((HttpServletResponse) response);

//依据请求路径获取相应的过滤器链 放到一个集合过滤器依次执行

List<Filter> filters = getFilters(fwRequest);//对这个代码进行相应的源码解析

if (filters == null || filters.size() == 0) {
                         if (logger.isDebugEnabled()) {
                         logger.debug(UrlUtils.buildRequestUrl(fwRequest)
                         + (filters == null ? " has no matching filters"
                           : " has an empty filter list"));
                             }

fwRequest.reset();

chain.doFilter(fwRequest, fwResponse);

return;
                       }

VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
                         vfc.doFilter(fwRequest, fwResponse);
                           }

private List<Filter> getFilters(HttpServletRequest request) {

//filterChains多个过滤器链   这个属性是 注入的<http pattern="" security="" />注入的,当然springsecurity会自动会加入一个过滤器链如上代码所示。

//一般把特殊的权限控制 <http>标签放到默认过滤器前面,不然的话 会被覆盖
                       for (SecurityFilterChain chain : filterChains) {

//根据请求的路径 获取相应的过滤器链  
                           if (chain.matches(request)) {

//返回第一个匹配的过滤器链的 过滤器集合 并在上面的代码中依次执行(FilterChainProxy的静态内部类中VirtualFilterChain.doFiler()依次执行)
                            return chain.getFilters();
                        }
                      }

return null;
         }

下面我们重点讲一下在默认过滤器链中UsernamePasswordAuthenticationFilter(登陆验证过滤器),和FilterSecurityInterceptor(权限管理拦截器).

 

usernamepasswordAuthenticationFilter的过滤器源码解析:过滤器的入口的doFilter,调用的是其父类的AbstractAuthenticationProcessingFilter的dofiler也就是usernamepasswordAuthenticationFiler没有重写父类的代码,dofilter代码如下:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;

//requiresAuthentication 方法匹配请求的路径,如果不是usernamepasswordAuthenticationFiler的默认的路径或者自己配置的路径直接跳过。

//默认的请求的url为j_spring_security_check,当然这个路径可以自己设置.

public UsernamePasswordAuthenticationFilter() {
super("/j_spring_security_check");
}

if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);

return;
}

if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}

Authentication authResult;

try {

//当请求的url匹配上了之后调用attemptAuthentication 方法,attemptAuthentication 是AbstractAuthenticationProcessingFilter的核心方法,在这个方法类进行

//登陆验证。这个方法在AbstractAuthenticationProcessingFilter 中是一个抽象方法,调用的是usernamepasswordAuthenticationFiler对其进行的实现attemptAuthentication下面看一看具体的登陆配置
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn‘t completed authentication
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
} catch(InternalAuthenticationServiceException failed) {
logger.error("An internal error occurred while trying to authenticate the user.", failed);
unsuccessfulAuthentication(request, response, failed);

return;
}
catch (AuthenticationException failed) {
// Authentication failed

//校验失败后调用  验证失败处理器(一般要自己实现)
unsuccessfulAuthentication(request, response, failed);

return;
}

// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}

//校验成功后 调用验证成功处理器(一般要自己实现)

successfulAuthentication(request, response, chain, authResult);
}

usernamepasswordAuthenticationFiler 中的attemptAuthentication的源码实现:

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

//默认是 是支持post请求可以设置属性postOnly,这一行的代码postOnly && !request.getMethod().equals("POST") 很经典,大家自己体会(充分利用&&操作符的执行过程)
                      if (postOnly && !request.getMethod().equals("POST")) {
                      throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
                      }

//获取用户名 默认参数为(j_username)

// 源码public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";

String username = obtainUsername(request);

//获取用户密码默认参数为(j_password)

                // public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";

               //这里的注意的是 用户的登陆的密码是没有加密的密码,数据库中或其他服务器中的密码肯定是加密密码 (防止数据泄露)

              //所以肯定要将客户端密码 加密之后再进行比对

String password = obtainPassword(request);

if (username == null) {
                      username = "";
                       }

if (password == null) {
                       password = "";
                          }

username = username.trim();

//这里用一个UsernamePasswordAuthenticationToken 对象存放登陆对象的信息注:UsernamePasswordAuthenticationToken实现了Authenticationj接口:

UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

                      setDetails(request, authRequest);

// Allow subclasses to set the "details" property

//设置一些请求的细节 如当前的sessionId和请求的地址信息  保存到 UsernamePasswordAuthenticationToken.details属性中:其实                  保存的是一个WebAuthenticationDetails对象.

return this.getAuthenticationManager().authenticate(authRequest);

//  getAuthenticationManager() 获得验证管理器 AuthenticationManager,获得的是这个实现类,默认的实现是ProviderManager类,调用的这个类authenticate()这个方法是整个验证就是在个这个方法中实现.

下面是这个类ProviderManager.authenticate()的源码:

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                       Class<? extends Authentication> toTest = authentication.getClass();
                       AuthenticationException lastException = null;
                       Authentication result = null;
                       boolean debug = logger.isDebugEnabled();

for (AuthenticationProvider provider : getProviders()) {

//遍历数据提供器 从配置中注入:

if (!provider.supports(toTest)) {
                             continue;
                          }

if (debug) {
                        logger.debug("Authentication attempt using " + provider.getClass().getName());
                        }

try {

// 从数据提供器中 校验数据 待会详细介绍 daoAuthenticationProvider(从数据库中获取验证校验信息,并进行比对)
                         result = provider.authenticate(authentication);

if (result != null) {
                           copyDetails(authentication, result);
                          break;
                     }
                    } catch (AccountStatusException e) {
                         prepareException(e, authentication);
                      // SEC-546: Avoid polling additional providers if auth failure is due to invalid account status
                            throw e;
                     } catch (InternalAuthenticationServiceException e) {
                         prepareException(e, authentication);
                           throw e;
                    } catch (AuthenticationException e) {
                       lastException = e;
                      }
               }

if (result == null && parent != null) {
                    // Allow the parent to try.
                 try {
                  result = parent.authenticate(authentication);
                  } catch (ProviderNotFoundException e) {
                 // ignore as we will throw below if no other exception occurred prior to calling parent and the parent
                  // may throw ProviderNotFound even though a provider in the child already handled the request
                  } catch (AuthenticationException e) {
                      lastException = e;
                  }
                  }

if (result != null) {
                         if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
                            // Authentication is complete. Remove credentials and other secret data from authentication
                           ((CredentialsContainer)result).eraseCredentials();
                  }

eventPublisher.publishAuthenticationSuccess(result);
                    return result;
                }

// Parent was null, or didn‘t authenticate (or throw an exception).

if (lastException == null) {
                     lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",
                    new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));
                }

prepareException(lastException, authentication);

throw lastException;
                  }

}

DaoAuthenticationProvider.authenticate()走的是其父类AbstractUserDetailsAuthenticationProvider.authenticate()的方法

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                      Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
                         messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
                                        "Only UsernamePasswordAuthenticationToken is supported"));

// Determine username
                     String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();

boolean cacheWasUsed = true;

                       UserDetails user = this.userCache.getUserFromCache(username);

                      //从缓存中加载数据,如果没有调用retrieveUser从数据库中获得这个数据,

if (user == null) {
                      cacheWasUsed = false;

try {

//从数据库总获取信息
                      user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
                    } catch (UsernameNotFoundException notFound) {
                    logger.debug("User ‘" + username + "‘ not found");

if (hideUserNotFoundExceptions) {
                         throw new BadCredentialsException(messages.getMessage(
                        "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                        } else {
                     throw notFound;
                  }
                 }

Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
             }

try {

       preAuthenticationChecks.check(user);

                        //校验一些细节,账号是否有效 账号是否被锁等等

                       additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
                        } catch (AuthenticationException exception) {
                            if (cacheWasUsed) {
                          // There was a problem, so try again after checking
                         // we‘re using latest data (i.e. not from the cache)
                         cacheWasUsed = false;
                        user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
                        preAuthenticationChecks.check(user);
                        additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
                           } else {
                            throw exception;
                         }
                        }

postAuthenticationChecks.check(user);

//比对密码

if (!cacheWasUsed) {
                           this.userCache.putUserInCache(user);
                              }

Object principalToReturn = user;

if (forcePrincipalAsString) {
                             principalToReturn = user.getUsername();
                           }

return createSuccessAuthentication(principalToReturn, authentication, user);
                      }

自此用户登陆彻底完成。

下面我们来看一下AbstractSecurityInterceptor权限管理的相关过程和源码解析:

我们首先来看这个的配置要:

<beans:bean id="securityInterceptor"
                        class="com.newtouch.security.web.access.intercept.FilterSecurityInterceptor"
                                      p:validateConfigAttributes="false" p:authenticationManager-ref="authenticationManager"
                                       p:accessDecisionManager-ref="accessDecisionManager"
                                        p:securityMetadataSource-ref="securityMetadataSource" />

这里要自定义注入三个 authenticationManager 验证管理器,accessDecisionManager 授权管理器,securityMetadataSource 加载资源数据器 (将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问)

首先进入FilterSecurityInterceptor的doFiler方法:

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 observe
                     // 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);
                  }

InterceptorStatusToken token = beforeInvocation(fi);

//重点看这个方法,下面贴出源码

try {
                  fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                   } finally {
                     finallyInvocation(token);
                  }

afterInvocation(token, null);
                   }
                }

这个方法走的是AbstractSecurityInterceptor的beforeInvocation

protected InterceptorStatusToken beforeInvocation(Object object) {
                                 Assert.notNull(object, "Object was null");
                                     final boolean debug = logger.isDebugEnabled();

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());
                }

Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);

//根据路径获取相应的权限配置 这个方法一般要自己实现

if (attributes == null || attributes.isEmpty()) {
                       if (rejectPublicInvocations) {
                         throw new IllegalArgumentException("Secure object invocation " + object +
                     " was denied as public invocations are not allowed via this interceptor. "
                                 + "This indicates a configuration error because the "
                              + "rejectPublicInvocations property is set to ‘true‘");
                  }

if (debug) {
                          logger.debug("Public object - authentication not attempted");
               }

publishEvent(new PublicInvocationEvent(object));

return null; // no further work post-invocation
                      }

if (debug) {
                     logger.debug("Secure object: " + object + "; Attributes: " + attributes);
             }

if (SecurityContextHolder.getContext().getAuthentication() == null) {
                                    credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
                             "An Authentication object was not found in the SecurityContext"), object, attributes);
              }

Authentication authenticated = authenticateIfRequired();

//确认用户身份是否验证,如果没有验证在调用验证管理器去验证

// Attempt authorization
                try {
                    this.accessDecisionManager.decide(authenticated, object, attributes);

//这个是授权方法 通过其子类去实现

//accessDecisionManager 有三种决策方法

//  AffirmativeBased 至少一个投票者必须决定授予访问权限
                 //ConsensusBased 多数投票者必须授予访问权限
                // UnanimousBased 所有投票者都必须投票或放弃投票授予访问权限(无投票表决拒绝访问)

//我们重点看一下AffirmativeBased 这个类的decide的方法源码实现:
                 }
                catch (AccessDeniedException accessDeniedException) {
                  publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException));

throw accessDeniedException;
                   }

if (debug) {
                 logger.debug("Authorization successful");
                }

if (publishAuthorizationSuccess) {
                     publishEvent(new AuthorizedEvent(object, attributes, authenticated));
                   }

// Attempt to run as a different user
                       Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);

if (runAs == null) {
                               if (debug) {
                     logger.debug("RunAsManager did not change Authentication object");
                }

// no further work post-invocation
                        return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
                } else {
               if (debug) {
               logger.debug("Switching to RunAs Authentication: " + runAs);
          }

SecurityContext origCtx = SecurityContextHolder.getContext();
              SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
              SecurityContextHolder.getContext().setAuthentication(runAs);

// need to revert to token.Authenticated post-invocation
              return new InterceptorStatusToken(origCtx, true, attributes, object);
           }
           }

//AffirmativeBased 的方法decide放法:

public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
                                 throws AccessDeniedException {
                       int deny = 0;

for (AccessDecisionVoter voter : getDecisionVoters()) {
                        int result = voter.vote(authentication, object, configAttributes);

//根据托票器来投票,返回结果,然后决策授权的结果,这个方法要决策成功与否

if (logger.isDebugEnabled()) {
                        logger.debug("Voter: " + voter + ", returned: " + result);
                  }

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();
           }

到此授权结束。

时间: 2024-10-11 10:36:57

SpringSecurity 依据用户请求的过程进行源码解析的相关文章

Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析

Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析 说明:Java生鲜电商平台中,由于服务进行了拆分,很多的业务服务导致了请求的网络延迟与性能消耗,对应的这些问题,我们应该如何进行网络请求的优化与处理呢? 到底有没有一些好的建议与方案呢? 下面这个文章将揭晓上面的问题,让你对SpringCloud微服务网络请求性能有一个全新的认识. 目录简介 01.网络请求异常分类 02.开发中注意问题 03.原始的处理方式 04.如何减少代码耦合性 05.异常统一处理步骤 06

Android xUtils3源码解析之图片模块

初始化 x.Ext.init(this); public static void init(Application app) { TaskControllerImpl.registerInstance(); if (Ext.app == null) { Ext.app = app; } } public final class TaskControllerImpl implements TaskController { public static void registerInstance()

Spring Security 解析(七) —— Spring Security Oauth2 源码解析

Spring Security 解析(七) -- Spring Security Oauth2 源码解析 ??在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security .Spring Security Oauth2 等权限.认证相关的内容.原理及设计学习并整理一遍.本系列文章就是在学习的过程中加强印象和理解所撰写的,如有侵权请告知. 项目环境: JDK1.8 Spring boot 2.x Spring Security

YTKNetwork源码解析

对于iOS开发者来说,就算是没有用过YTKNetwork框架,应该也见过,听过了.它是猿题库技术团队开源的一个网络请求框架,内部封装了AFNetworking.它把每个请求实例化,管理它的生命周期,也可以管理多个请求. 在正式讲解源码之前,我会先讲一下该框架所用的架构和设计模式.我总觉得对架构和设计有一定的了解的话,会有助于对源码的理解. 1. 架构 先上图: YTKRequest架构图 在这里简单说明一下: YTKNetwork框架将每一个请求实例化,YTKBaseRequest是所有请求类的

Android-AndFix 热修复框架原理及源码解析

AndFix原理 AndFix的原理就是通过c++指针进行方法的替换,把有bug的方法替换成补丁文件中的方法.  注:在Native层使用指针替换的方式替换bug方法,已达到修复bug的目的. 使用AndFix修复热修复的整体流程: 方法替换过程: 源码解析 解析源码从使用的方法一一解析. 在自定义Application中初始化PatchManger: PatchManager mPatchManager = new PatchManager(); 1 1 直接实例化了一个PatchManger

AndFix Bug热修复框架原理及源码解析

?? AndFix原理 AndFix的原理就是方法的替换,把有bug的方法替换成补丁文件中的方法.  注:在Native层使用指针替换的方式替换bug方法,已达到修复bug的目的. 使用AndFix修复热修复的整体流程: 方法替换过程: 源码解析 解析源码从使用的方法一一解析. 在自定义Application中初始化PatchManger: PatchManager mPatchManager = new PatchManager(); 1 1 直接实例化了一个PatchManger实例对象,接

CBV-2-CBV流程-View源码解析

CBV是基于反射实现根据请求方式不同,执行不同的方法. 请求流程:view源码解析 1.urls.py :请求一定来执行视图下的as_view方法.也可以直接点击as_view()来找源码. 2.views.py 视图内没有as_view方法,则找父级的as_view方法. 3.源码:as_view返回自己下面的view方法,as_view执行了自己view方法,返回值是dispatch方法. 4,dispatch方法判断请求方式. 5,所以请求已经来,第一步先执行的都是dispatch方法.

CBV-2-CBV流程-view源码解析-面向对象-继承

CBV-2-CBV流程-view源码解析-面向对象-继承 CBV,基于反射实现根据请求方式不同,执行不同的方法. 请求流程:view源码解析 1.urls.py :请求一定来执行视图下的as_view方法. 2.views.py 视图内没有as_view方法,则找父级的as_view方法. 3.源码:as_view返回自己下面的view方法 4.as_view执行了自己view方法,放回值是dispatch方法. 5.dispatch方法判断请求方式. 6.所以请求已经来,第一步先执行的都是di

Tomcat请求处理过程(Tomcat源码解析五)

前面已经分析完了Tomcat的启动和关闭过程,本篇就来接着分析一下Tomcat中请求的处理过程. 在开始本文之前,咋们首先来看看一个Http请求处理的过程,一般情况下是浏览器发送http请求->建立Socket连接->通过Socket读取数据->根据http协议解析数据->调用后台服务完成响应,详细的流程图如上图所示,等读者读完本篇,应该就清楚了上图所表达的意思.Tomcat既是一个HttpServer也是一个Servlet 容器,那么这里必然也涉及到如上过程,首先根据HTTP协议