本文从一个web请求如何到达服务器,请求经历了哪些过程,最后是如何通过我们的授权验证(用cas认证举例),通过一步一步解析整个过程,同时给出相对应的类图和时序图来方便理解,最后做一个总的概括来进一步加强理解。本文的内容来自对源码的理解,如果有不正确的地方,可以在下方留言,我会及时修改,谢谢。阅读本文大约90分钟。
1.1 tomcat的类图结构tomcat的结构关系就是一个Server包含多个Service,Service指向一个Container,Service和Container的连接通过Connector实现。tomcat的顶层接口是lifecycle、container,其中lifecycle控制所有子容器的生命周期,container是每个具体容器的抽象。
可以从startup.sh里面发现tomcat的入口是Bootstrap里面的main方法。 Catalina是Bootstrap的代理类来实现真正的启动逻辑。
Server,Service,Engine,Host,Context依次是包含关系。他们自身实现了特定的功能方法。
ContainerBase是一个抽象类,实现了所有容器类都用到的公共方法。
Connector是Service的委托实例,可以看到StandardService里面是一个Connector[]的数组,主要用于监听端口,接收外部的请求。
ProtocolHandler是Connector的委托实例,用于进行请求相对应的协议来进行后续的处理。
下面是主要类图的结构,从上到下的实现关系。1.2. tomcat的启动tomcat的启动主要分为容器的init和start两个部分。每个过程都是通过父容器先初始化,然后逐个通过for循环来初始化子容器,中间还包含一些相关的事件出发。
下面是从Bootstap到Pipeline启动的过程 我们通常的web.xml的解析就是在StanderdContext的start里面实现的,通过委托给ContextConfig解析所有的元素初始化Context变量。 下面是从Pipeline到Servlet的过程 1.3. tomcat的请求 当我们通过浏览器想服务器发送一个请求后,浏览器会帮我们生成一个http的报文,最终通过tcp发送到我们的服务端的监听端口,服务端监听到端口[connector]有数据进来便会从线程池里面选着一个线程来处理,然后把数据交给tomcat的对应Processor来处理,这里就是http11Processor来解析报文头,然后交给CoyoteAdapter,CoyoAdapter通过Mapper类进行Engine的选择,最后把request交给匹配的Engine来处理。从Engine到Wrapper的调用链路就是一个连续的过程[Pipeline的责任链模式]。最后由Wrapper交给fiIter及servlet进行处理。整个过程就是下面的时序图的流程。 1.4. StandardContext的reload过程 当我们设置了tomcat的server.xml里面的context的reloadable为true时,war被修改后,容器就监听到class文件有变更,首先就是把Context的状态改为暂停,这时候拒绝执行外部的访问,然后依次的停止servlet、filter、contextlistener,接着是WebappClassLoader进行class的重新加载,最后就是依次的init、start。下面是完整的流程图。2.1. spring boot 启动过程spring boot启动的过程和spring启动过程类似,只是在refresh(context)里面增加了embededtomcat的启动过程。spring自身的启动这里不讲述,下面主要是spring里面是如何启动embededtomcat的。关键点就是在初始化applicationcontext的时候通过EmbeddedServletContainerFactory来启动Tomcat,然后依次设置Tomcat需要的各种参数,最后把当前的context添加到tomcat里面,当前的context已经包含了在spring里面注册的filter以及filter,这个过程和上面的独立tomcat服务的启动顺序是相反的,但是实质是一样的。下面是主要的过程图及相关的代码注释。
tomcat的类结构和embededTomcat的类的对应关系。
2.2. spring boot的请求过程 和tomcat的请求过程一致,这里重点讲述下Filter的过程,因为后面主要是围绕security来进行的。filter的过程就是通过filterChain来进行的,这里要注意一点的是filterChain的初始化是在第一次请求到来的时候做的,因为filterChain初始化后会放在org.apache.coyote.Request类的Note里面,后续的请求直接从request.getNotes(1)里面获取,这样不用每次request都创建filterChain。所有的filter在一个filters数组里面,直到所有的filter都进行了才会把请求交给servlet来处理,所以security的认证过程一定是在filter里面做的。3.1. spring security的安全认证spring security认证的体系结构的顶层接口是AuthencationManager,然后具体的认证过程是通过委托给一系列AuthenticationProvider来实现的。AuthenticationProvider提供了对具体认证需要的信息查询的接口,我们可以实现UserDetailService里面达到查询我们需要的目标数据。当认证过程成功后就会生成一个AuthenticationToken,这个token就代表了请求的安全凭证。token会放入SecurityContext里面,然后生成sessionId,httpsession的熟悉里面设置了springSecurityContxtKey的值为当前的SecurityContext,下次请求进来的时候通过sessionId查询到httpSession,然后通过httpSession和springSecurityContxtKey就能拿到对应的securityContext。
下面是authencation的类图结构。3.2. 认证流程security的认证入口点是SecurityContextPersistenceFilter。首先尝试从ContextRepository里面获取,如果有的化直接把securitContext设置到当前的threadLocal里面;如果没有就继续执行后面的认证Filter来创建,这里举例交给UsernamePasswordAuthenticationFilter来处理,UsernamePasswordAuthenticationFilter通过调用DaoAuthenticationProvider来做具体的认证过程,DaoAuthenticationProvider又通过UserDetailService来从db里面查询用户名和密码,然后和当前的提交参数对比,如果一致就认证通过。下面是完整的流程图。
3.3. cas的认证原理 首先了解几个概念,cas client就是我们的应用服务,cas server是实现cas认证的服务器。浏览器就是我们发起请求的地方。当我们通过浏览器向cas server发起请求,首先会通过authencationFilter来处理请求,authencationFilter发现没有cas票据,会重定向到cas server,如果之前没有登陆过cas server就会出现提示输入用户名密码,这时候cas server会生成TGT、ST、TGC,然后在重定向到cas client,后面的认证逻辑就交给CasAuthencationFilter来处理。 下面是请求的流程图。CasAuthencationFilter的认证是通过CasAutenticationProvider来实现的。CasAuthencationFilter拦截路径是通过我们设置的servcie的path来判断的。授权的关键点就是下面的userDetailsChecker.check(userDetails)这个方法,这个方法里面会通过ticketValidator来验证cas server提供的ST是否有效,具体过程是带上service和ticket参数,service值是当前应用的服务地址,ticket值就是cas server之前生成的st,请求的地址就是cas server,path是serviceValidate,请求发送到cas server后,cas server根据之前用户访问生成的TGT来匹配当前请求,如果匹配成功就返回一个报文,里面包含了cas:authenticationSuccess的标签,同时返回了当前的用户cas:user,这个标签的值一般是登陆用户的id,接下来CasAutenticationProvider会调用userDetailService服务,userDetailService我们通常会直接实现自己的逻辑,比如通过cas:user查询具体的用户信息来设置相应的权限等。最终生成一个CasAuthenticationToken,里面有principal、userDetails、credentials(就是cas server生成的st)等信息,然后这个token会添加到httpSession的属性里面。
下面是CasAutenticationProvider的类图结构。本文讲述了tomcat的启动以及请求过程、spring security的结构体系、cas的认证过程,由于每个部分的知识点都很广泛,不能面面俱到,只是抽取了部分内容进行重点讲述,更多的内容会在后面逐渐补充。本文写作的动机主要是处于个人对知识的总结,同时也希望能给看到这篇文章的同学一些帮助。我记得当时自己在做项目接入cas的时候,由于几个配置问题,花了不少的时间,最后总结下来是由于对知识点的掌握不到位造成的,最后慢慢把问题解决,然后逐步分析里面的实现逻辑,其实现在想来很多知识其实并不难,从开始的时候就进行一些有意义的思考然后解决问题将会事半功倍。现在自己总结了一个座右铭“不要把复杂的问题想的太简单,也不要把简单的问题想复杂”,希望这句话也能帮到你。