1.1spring security整体架构
-
简介:就是spring security的整个流程架构,基本可以分为三个部分来介绍,
-
一个是spring security 是如何在servlet filter和application context之间建构一个桥梁,使得application cotext的定义的filter bean 构成一个SecurityFilterChain,能够注入到serlvet filter中,从而可以实现过滤;
-
第二部分:详细介绍,一个请求进入到SecuriyFilterChain后,里面的filter是如何协同过滤的,从而来实现用户的身份是否authentication 和athorization
-
第三部分:Authentication的流程,以及如何去自定义验证的逻辑
1.2 第一部分的整体架构
- 图例
-
文字描述过程
DelegatingFilterProxy
作为spring serurity的作用的入口,其作为filter bean注入到servlet container的filterchain中,DelegatingFilterProxy
通过获取ioc容器中beanName为springSecurityFilterChain
的FilterChainProxy
bean,把工作委托给FilterChainProxy
来完成FilerChainProxy
通过迭代挑选出最先匹配的SecurityFilterChain
,然后交给这个链去执行security的检测
-
代码分析(以springboot为例,导入spring-security-starter)
-
DelegatingFilterProxy
被注册到容器中的,以及其是如何作为作为桥梁的解析:
在springboot的自动配置中时注入
DelegaitingFilterProxyRegistrationBean
类型的bean目前暂时认为:就是servlet container容器在启动的时候,就会自动搜收
DelegatingFilterProxyRegistrationBean
bean,然后调用该bean的getFilter()的方法,把返回的newDelegatingFilterProxy
实例注入到filterChain,中,从而实现spring security的接口 -
DelegatingFilterProxy
是如何把工作交给FilterChainProxy
处理的解析:
在
DelegatingFilterProxy
在initDelegate()
方法,通过beanName(springSecurityFilterChain
)来获取容器中的FilterChainProxy
bean在
DelegatingFilterProxy
有doFilter()方法,在该方法中可以通过该方法把该工作代理给FilterChainProxy
-
FilterChainProxy
是在哪里把其注册到容器中的呢 -
FilterChainProxy
中的doFilter
具体是如何工作的FilterChainProxy本质一个bean filter,在其
doFilter
的方法中,迭代多条SecurityFilterChain
,把第一个match(request)正确的securityFilterChain
中的filtes获取出来,以virtalFilerChain
的方式把执行这些filters,执行完这些filters后,则接着回到执行整个serlvet contianer 中的FilterChain中-语句chain.doFilter(fwRequest, fwResponse);
-
1.3 第二部分-----SecurityFilterChain
整体流程架构
-
简介:这节主要将的是,请求进入到一条
SecurityFilterChain
中,执行的流程是什么 -
执行的流程
-
spring security 中提供的filter的执行顺序的依据是什么
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xo6cQ0j7-1601970757613)(E:\typora\image\image-20200609111406635.png)]
-
通过debug
anyPathRequest
查看请求在多个filter中执行的痕迹logging: level: org.springframework.security.web.authenticatio: debug org.springframework.security.web.util.matcher: debug org.springframework.security.authentication.dao: debug
通过这些,就可以查看
-
-
案例(分析其执行的流程)
-
案例描述:有三条自定义的securityFilterChain,对其中一条链进行分析
-
流程
-
启动项目
-
访问
http://localhost:8080/html/login1.html
,login1.html
位于的目录是static/html
-
在login1.html中登录,访问"/admin/login"
代码解析,可以看到login请求,在进入到多条过滤链中,首先看是否匹配在websecurity中配置的不拦截静态资源的地址(即请求不会进入到过滤链中执行,一般只要一条链配置就可以啦,就可以达到不拦截的效果),然后进入到1过滤链,请求不匹配,不进入,2也是这样,由于3是任何请求都匹配的,所以进入到3链中
进入3链后,该login请求经过logout filter,判断与logout filter中拦截的地址有没有匹配,从图看出,不匹配,然后执行FormLogin中,匹配,验证成功,然后重定向到loginSeccess地址,相当于浏览器重新发一次请求,然后请求/login的流程的过程也是一样的,然后在FilterSecurityInterceptor中,匹配anyrequest,由于其已经登录成功,所以带有成功
SessionId
,此时,在SecurityContext有Authentiacation,依此来判断,成功,则放行,接下来往下执行,最后就会到selvet中,映射到相应的controller中 -
如果登录不成功怎么样
由于在FormLogin中配置对于LoginFail配置的失败后转发的地址是permitAll的,所以在FilterSecurityInterceptor就会被放行(注意:在这里的permitAll与在authorizeRequests()配置的permitAll起到的作用不一致,后面有讲到)
-
如果在没有登录的情况下,直接访问/admin/hello
-
总结
就是如果该请求在不登录的情况下,直接访问,在FilterSecurityIncerptor进行拦截的时候,如果在FilterSecurityIncerptor有配置该地址的permitAll,则匹配,直接放行,如果没有配置,则会匹配到anyRequst(),中(前提:anyRequest().authentication()),判断是否登录(SecurityContext已经有Authentcation),如果有,则放行,如果没有则抛出异常,被ExceptionTranslationFilter接收到该异常,通过authenticationentryPotin中配置的地址,重定向该地址,从而可以处理该异常,但如果在登录(SecurityContext已经有Authentcation)的情况下,则在FilterSecurityInceptor就会被放行
注意anyRequst(),中(前提:anyRequest().anyAuthority(“ADMIN”)),则在会匹配到anyRequst(),后,不但会判断其是否登录,也同时判断其Authentication中的authority中是否是ADMIN,如果没有登录,则抛出异常,被ExceptionTranslationFilter接收到该异常,但如果登录了,authority不符合,则直接产生/error(其不是重定向的)
-
-
1.4 authenticaiton的流程
-
基本的过程
就是在进行验证之前,先构造一个
AuthenticationToken
(例如:前端页面传来的username和password),然后把AuthencationToken
交给ProviderManager
(是AuthenticationManager
的实现类)用来Authenticate,不过其委托给ProviderManager
来处理的,验证成功后,会返回一个Authentication
,之后把该Authentication
加到SecurityCo