在试题系统开发过程当中,认证方式愈来愈完善,也对Spring Security
有了更加深入的理解。前端
本文,咱们一块儿来领略Spring Security
的设计原理。java
Servlet
是Java Web
领域中的软件开发规范,Tomcat
是实现Servlet
规范的Java Web
服务器。安全
package javax.servlet; public interface Servlet { public void init(ServletConfig config) throws ServletException; public ServletConfig getServletConfig(); public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; public String getServletInfo(); public void destroy(); }
Servlet
长这样,生命周期十分简单,初始化init
、业务逻辑service
、销毁destroy
。服务器
简单来讲:咱们根据Servlet
接口开发咱们的应用,服务器开发商根据Servlet
接口开发Servlet
服务器。架构
前端HTTP
请求,Tomcat
选择路由匹配的Servlet
,若是已经实例化,直接调用service
;若是未实例化,实例化后调用service
,处理完以后返回响应。并发
图中示例的url
统一采用小写,关于url
大小写的问题,如下是结论,虽然有些url
是大小写不敏感的,可是这使得表示惟一性变得困难,咱们一般应该认为大小写是敏感的。框架
There may be URLs, or parts of URLs, where case doesn't matter, but identifying these may not be easy. Users should always consider that URLs are case-sensitive.
为了在Servlet
先后执行其余逻辑,定义了Filter
接口,在请求先后都会执行。ide
从Spring Security
官方文档摘抄的一张图:高并发
Spring Security
的原理其实就是经过Servlet
中的Filter
技术进行实现的,经过一系列内置的或自定义的安全Filter
,实现接口认证与受权。性能
Spring Security
官方也给出了默认Filter
的执行顺序,先执行认证的过滤器,后执行受权的过滤器,其中就有咱们经常使用的UsernamePasswordAuthenticationFilter
、BasicAuthenticationFilter
等等。
阅读源码一个Filter
源码便可理解整个认证架构设计。
注:如下代码中部分无关代码已被删减。
public class BasicAuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { try { /** 从请求中获取用户名密码信息 */ UsernamePasswordAuthenticationToken authRequest = authenticationConverter.convert(request); /** 若是没有相关信息,说明不是此种认证方式,执行后续过滤器 */ if (authRequest == null) { chain.doFilter(request, response); return; } /** 获取用户名 */ String username = authRequest.getName(); /** 判断该用户是否须要认证 */ if (authenticationIsRequired(username)) { /** 尝试使用 token 进行认证 */ Authentication authResult = this.authenticationManager .authenticate(authRequest); /** 认证成功,将认证结果置入上下文 */ SecurityContextHolder.getContext().setAuthentication(authResult); /** 认证成功相关回调 */ this.rememberMeServices.loginSuccess(request, response, authResult); onSuccessfulAuthentication(request, response, authResult); } } catch (AuthenticationException failed) { /** 认证失败,清空当前上下文信息 */ SecurityContextHolder.clearContext(); /** 认证失败相关回调 */ this.rememberMeServices.loginFail(request, response); onUnsuccessfulAuthentication(request, response, failed); /** 若是须要忽略失败,则继续执行后续过滤器 */ if (this.ignoreFailure) { chain.doFilter(request, response); } /** 不然开始执行新的认证方案 */ else { this.authenticationEntryPoint.commence(request, response, failed); } return; } /** 本过滤器执行完毕,执行后续过滤器 */ chain.doFilter(request, response); } }
其实很简单是否是?
若是默认的验证方式不知足要求,要怎么添加自定义验证方式呢?其实只须要添加自定义的Filter
便可。
就好比说常见的短信验证码登陆方式:
默认不支持短信方式,咱们能够在过滤器链中植入一个自定义的短信验证过滤器,认证成功后设置认证信息便可。
SecurityContextHolder.getContext().setAuthentication(authResult);
这个是上个月遇到的问题,尝试了一下OAuth 2.0
认证架构。
其实这个架构很广泛,许多项目都采用该种架构。只不过都是采用Spring Cloud Netflix Zuul + Spring Security Resource Server
的实现。
这里我尝试将Zuul
换成Spring Cloud Gateway
,由于Zuul
的阻塞IO
在网关层面实在太影响性能了。
前面已经说了,Spring Security
中的认证与受权方式是经过Servlet
技术套装中的Filter
实现的,Tomcat
提供了Servlet
的运行环境。
Spring Cloud Netflix Zuul
集成Spring Boot Starter Web
也就是默认的Tomcat
实现网关,因此Zuul
中是有Servlet
的运行环境的。
而非阻塞的Spring Cloud Gateway
基于Spring WebFlux
框架,不支持Servlet
,底层采用高性能的Netty
服务器。
Spring WebFlux
与Spring MVC
的对比请看下图:
因此当Spring Security
与Spring Cloud Gateway
集成时,就会出错,Spring Security
须要的Servlet
环境没有被知足。
这也是以前对Reactive
的理解不到位而引发的错误,WebFlux
环境下,应该集成Spring Security Reactive
,而非默认的Spring Security
。
经资料查阅,高性能的 Netty
十分受各大互联网企业欢迎,Twitter
、Facebook
、苹果、微博都在使用Netty
,具体Netty
为何更适合高并发?之后咱们一块儿学习。