对于一个完善的系统,使用会有不少人使用:最起码也会包含用户端和后台管理端两个角色,固然随着业务不断复杂,确定会产生更多的角色,这些角色被赋予不一样的权限,只能操做系统中的某一部分功能,而后全部角色一块儿来操做各自的模块,整个系统才能处于一个正常的活跃状态。 对于一个系统的登陆方式,从帐号密码登陆,邮箱密码登录,到很是方便的第三方登陆方式:好比掘金的第三方登陆就使用了:微信、GitHub、微博。应该是能够更加方便的引流吧,去除繁琐的注册流程。 除此以外,登陆系统的设计还包含在一个分布式系统中的单点登陆SSO,使用CAS进行处理,还有jwt等等这些均可以集成在SpringSecurity中,最近连续看了好几天的Security源码(以前粗略的看过一遍,基本没理解),发现好复杂啊,好绕,功能强大势必会致使复杂的配置,因此在有些场景下使用就会觉着重,可是权限设计又是每个系统都须要的,因此决定在这里仔细缕一缕。html
从listener-filter-servlet中的filter提及,security的入口实际上是filter的FilterChain
中的一个filter。java
FilterChain: is an object provided by the servlet container to the developer giving a view into the invocation chain of a filtered request for a resource.Filters use the FilterChain to invoke the next filter in the chain, or if the calling filter is the last filter in the chain, to invoke the resource at the end of the chain.react
ApplicationFilterChain: Implementation of FilterChain used to manage the execution of a set of filters for a particular request. When the set of defined filters has all beenexecuted, the next call to doFilter() will execute the servlet's service() method itself.web
先来看一下通常都有哪些filter,以下图所示,其中有些是我引入Actuator监控模块加入的,先不要care,下次单独说一下他是干吗的spring
其中有个filter:DelegatingFilterProxy,这是一个代理,那么代理的是谁呢?内部持有的FilterChainProxy,它作该过滤器应该作的事情,那么这个过滤器干吗了呢?咱们须要了解一个FilterChainProxy这个类(👇),这个就是咱们要security的入口,而后下面咱们从这个入口开始一点一点的分析。express
FilterChainProxy 介绍安全
SecurityFilterChain 介绍微信
首先,在上面介绍中说到filterChainProxy内部的List<SecurityFilterChain> filterChains
,须要从这个过滤器链的集合中匹配出当前请求的那个过滤器链,有个关键:RequestMatcher(👇),而后从将匹配到的securityFilterChain中的filter集合拿出来,便开始了进行了过滤,以下图所示分布式
RequestMatcher 介绍ide
经过下图看下这些默认过滤器的功能,没有详细看过,后续再谈:
UsernamePasswordAuthenticationFilter
这个类就是验证的关键, 第一步:调用父抽象类的doFilter方法:
第二步:调用子类实现的attemptAuthentication方法:
ProviderManager
,它持有一个List providers,这些provider提供了不一样的认证方式,能够自定义
AuthenticationProvider
进行认证
咱们来看一下这些authenticationProvider都是干吗的
DaoAuthenticationProvider
:数据访问认证方式,也就是说能够从不一样的数据源来进行认证, 从上图中也能够看到父类
AbstractUserDetailsAuthenticationProvider
,提供入口authenticate()方法, 而后定义一些抽象方法,子类去实现具体的逻辑,又是模板模式的体现,在
DaoAuthenticationProvider
中有个很重要的属性:
UserDetailsService
,则会个接口里面只有一个方法:
loadUserByUsername
,而后不一样的服务能够采起不一样的策略来实现,对,这里我感受就是一个策略模式,
FilterSecurityInterceptor 感受也很绕,须要看下那个投票究竟是干吗的, 核心:invoke()方法
this.accessDecisionManager.decide(authenticated, object, attributes);
//主要是基于AccessDecisionVoter的一个投票过程
//在已经确保用户经过了认证,如今基于登陆的当前用户信息,和目标资源的安全配置属性
//进行相应的权限检查,若是检查失败,则抛出相应的异常 AccessDeniedException
复制代码
对于SpringBoot项目进行分析,使用SpringBoot以后最强大的功能即是自动配置,引入security的stater以后,什么都不用干,访问咱们的以前的controller接口,就发现已经被保护起来,缘由就是这些自动引入的以及经过它们又间接引入的配置:
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
复制代码
说一个主要的自动配置:
/** * Interface for building an Object * @param <O> The type of the Object being built */
public interface SecurityBuilder<O> {
/** * Builds the object and returns it or null. * @return the Object to be built or null if the implementation allows it. * @throws Exception if an error occurred when building the Object */
O build() throws Exception;
}
复制代码
继承实现:
/** * Allows for configuring a {@link SecurityBuilder}. All {@link SecurityConfigurer} first * have their {@link #init(SecurityBuilder)} method invoked. After all * {@link #init(SecurityBuilder)} methods have been invoked, each * {@link #configure(SecurityBuilder)} method is invoked. * @param <O> The object being built by the {@link SecurityBuilder} * @param <B> The {@link SecurityBuilder} that builds objects of type O. This is also the * {@link SecurityBuilder} that is being configured. */
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
/** * Initialize the {@link SecurityBuilder}. Here only shared state should be created * and modified, but not properties on the {@link SecurityBuilder} used for building * the object. This ensures that the {@link #configure(SecurityBuilder)} method uses * the correct shared objects when building. * @param builder * @throws Exception */
void init(B builder) throws Exception;
/** * Configure the {@link SecurityBuilder} by setting the necessary properties on the * {@link SecurityBuilder}. * @param builder * @throws Exception */
void configure(B builder) throws Exception;
}
复制代码
继承实现:
O
这个类型的对象的; SecurityConfigurer 是用来对不一样的build过程当中的属性进行配置; 这里面包含有众多的类,咱们说一些重要的:
参考:
www.shangyang.me/categories/… www.jianshu.com/u/fb66b7412… zhuanlan.zhihu.com/c_111502114… docs.spring.io/spring-secu…