1.基础知识点
为了理解Security Oauth2 我们要先理解Security ;理解Security ,我们需要先做一些Security组件等基础认识的认识。
Security组件的认识
- SecurityContextHolder:用来存储安全上下文信息,Spring Security 的校验之后,验证信息存储在SecurityContext
- Authentication: 在Security领域,用户密码等不叫做用户密码,叫做Authentication。Authentication 在 spring security 中是最高级别的身份 / 认证的抽象。 在进入Security领域前,得把信息封装成Authentication.(如果用一成语来形容,就是入乡随俗),例如我们常用的
[clientId,clientSecret ] [username,password]
都可以封装成一个(Authentication)UsernamePasswordAuthenticationToken
- AuthenticationManager: 从组件层面讲,此组件是用来校验
Authentication
,常见实现类有ProviderManager。但是从代码层面,ProviderManager 将校验的工作交给了另一个组件AuthenticationProvider来完成的。ProviderManager 中维护一个List
,通过遍历找到支持当前Authentication
认证的AuthenticationProvider,交给其进行认证。(这样看AuthenticationManager可以看做是大总管) - AuthenticationProvider: 就是上面说的代码层面真正对
Authentication
进行校验的组件。常见的AuthenticationProvider有
(1.DaoAuthenticationProvider: Dao式认证Provider ,从数据库中取出信息与提交的信息进行比对,完成认证。
(2. AnonymousAuthenticationProvider: 匿名认证Provider - UserDetails : 理解UserDetails,我们可以对比Authentication来理解, Authentication是来自用户提交的数据封装,UserDetails 是从数据层获取的用户信息封装。
- UserDetailsService :
DaoAuthenticationProvider
认证器从数据库层取数据是通过UserDetailsService 完成的,取到的是UserDetails 。常见的UserDetailsService
(1.JdbcDaoImpl
(2.ClientDetailsUserDetailsService: 查询[clientId,clientSecret ]形式的UserDetails
Filter-Servlet
我们都知道请求经过Filter链到Servlet
过程看起来是这样
Security原理
牢记,Spring Security在web应用中,是通过filter 介入的。
为了介入到主体ApplicationFilterChain中,这里要介绍一个特殊的Filter ,
DelegatingFilterProxy:
这个Filter很有意思,他内部有一个Filter delegate
属性 ,用来代理另一个Filter。当请求执行到DelegatingFilterProxy时,会调用delegate 这个Filter 。
DelegatingFilterProxy可以看做是一个可以让Filter链拐弯的 Filter
FilterChainProxy:
也是一个Filter , Security就是通过把他设置到DelegatingFilterProxy.delegate
属性上来介入了主体FilterChain .但是从他名称来看他本身也是一个代理性质的FIlter
他内部维护了一个List filterChains
来表示不同权限的url对应的不同的过滤器链 ,但是一次请求最多只有一个SpringSecurityFilterChain链。
SpringSecurityFilterChain:
FilterChainProxy遍历List filterChains
匹配到一个适用于当前请求的SecurityFilterChain然后就是链式调用了。
Security 常见Filter:
- SecurityContextPersistenceFilter: 位于SecurityFilterChain的顶端。以前我们使用session来存储用户信息,用了Security框架后,用户登录一次,后续通过sessionId 来识别,用户信息存放到SecurityContextHolder中,这个放入的过程就是SecurityContextPersistenceFilter完成的。SecurityContextPersistenceFilter的主要工作创建
SecurityContext
安全上下文信息和请求结束时清空SecurityContextHolder
(用了try,finally组合) - UsernamePasswordAuthenticationFilter:表单认证是最常用的一个认证方式,允许表单输入用户名和密码进行登录。他会先将
[username,password]
封装成Authentication然后交给authenticationManager 认证,authenticationManager 会选择一个provider ,通过UserDetailsService从redis获取mysql等数据层面获得存储用户信息的数据的UserDetail与Authentication 进行对比。认证成功 - ExceptionTranslationFilter: 异常转换过滤器位于整个 springSecurityFilterChain 的后方,主要处理两大异常,AccessDeniedException 访问异常和 AuthenticationException 认证异常。根据配置和异常类型,会选择跳转到登录页面,或者404 ,405页面。
代码层面的集成:
代码中使用Security 最重要的是@EnableWebSecurity ,这是代码集成Security的重要注解,我们来看看
Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) @Target(value = { java.lang.annotation.ElementType.TYPE }) @Documented @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class }) @EnableGlobalAuthentication @Configuration public @interface EnableWebSecurity { boolean debug() default false; } --- @Import(AuthenticationConfiguration.class) @Configuration public @interface EnableGlobalAuthentication { }
@EnableWebSecurity注解的工作就是激活三个类
- SpringWebMvcImportSelector: 判断当前的环境是否包含 springmvc
- WebSecurityConfiguration: 是用来配置 web 安全的
- AuthenticationConfiguration: 配置认证相关的核心类,主要负责生成全局的身份认证管理者 AuthenticationManager
最重要的就是WebSecurityConfiguration
WebSecurityConfiguration
在这之前介绍一个阅读Spring相关框架的源码技巧
xxConfiguration: 这种格式的配置文件,我们可以看做是一个bean.xml文件,对容器输出Bean
xxConfigurer: 这种格式的配置文件,通常是要被xxConfiguration获取到。xxConfiguration文件从xxConfigurer中提取配置,统一到xxConfiguration中处理。
一句话总结:xxConfiguration会搜集N个相关的xxConfigurer到本类中解析他们,统一成一个xxConfiguration配置文件对容器输出Bean.
好,下面我看看WebSecurityConfiguration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware { //搜集SecurityConfigurer到本类中,做集中解析。 private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers //输出springSecurityFilterChain bean @Bean(name = "springSecurityFilterChain";) public Filter springSecurityFilterChain() throws Exception { boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty(); if (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { }); webSecurity.apply(adapter); } return webSecurity.build();//构建FilterChainProxy } }
两个重要点:
- 搜集相关的SecurityConfigurer: 我们集成security时,通常会继承WebSecurityConfigurerAdapter做安全配置,WebSecurityConfigurerAdapter本身实现了SecurityConfigurer,这样们的配置信息会被解析到WebSecurityConfiguration配置类中,作用到security中。所以这就是为啥我们要实现一个WebSecurityConfigurerAdapter来配置安全策略的原因。
- 输出(FilterChainProxy)springSecurityFilterChain bean : 这样就算是在代码层面与主题FilterChain对接上了。(springSecurityFilterChain的创建过程有兴趣的可以跟下代码)
WebSecurityConfigurerAdapter适配器模式的运用,使我们可以选择性的实现部分配置。
2.Security 执行流程图
在此理论知识,代码知识的基础上,我直接贴出流程图
3.Security Oauth2 原理
看完了Security的原理,我们在看看oauth2的原理。
- Security Oauth2 如何架设在Security框架之上?
- Security Oauth2 又发生了哪些变化呢?
- Oauth2的四种认证方式是如何实现的?
带着这些疑问,我们还是从组件的认识到执行流程图
组件认知
TokenEndpoint:
理解为一个Controller ,/oauth/token接口就是在这里。 提到Controller,我们就知道,这就是业务逻辑处理地方,也就是Oauth逻辑处理的地方。
TokenGranter:
组件层面意思是令牌授予者,Oauth2规范的实现就是此组件实现的
TokenServices :
定义了对Token的一些操作,创建,获取,刷新。我们把他理解为Sevevice层。
- AuthorizationServerTokenServices : 授权服务器端用到的tokenSerrvices
- ResourceServerTokenServices: 资源服务器端的tokenservices
这样这三个组件的关系体系就出来:
请求到Controller ,交给TokenGranter进行授权,TokenGranter授权过程中调用TokenServices 生成token。
代码层面的认知:
集成oauth2,我们会多出两个配置类来,为什么???
//权限服务器配置 @Configuration @EnableAuthorizationServer protected static class MyAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { } //@EnableAuthorizationServer注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class}) public @interface EnableAuthorizationServer { } //资源服务器配置 @Configuration @EnableResourceServer protected static class MyResourceServerConfiguration extends ResourceServerConfigurerAdapter { } //@EnableResourceServer注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ResourceServerConfiguration.class) public @interface EnableResourceServer { }
权限服务器配置:
@EnableAuthorizationServer注解:此注解主要是激活两个配置类
- AuthorizationServerEndpointsConfiguration : 配置TokenEndpoint 等Controller类,也就是注册Controller式Bean,例如/oauth/token接口
- AuthorizationServerSecurityConfiguration: 间接实现了SecurityConfigurer接口。SecurityConfigurer接口在上面说过, 会在启用Security时,被WebSecurityConfiguration配置类搜集解析。这样Oauth2的配置与Security配置体系关联起来了。
MyAuthorizationServerConfiguration:继承于AuthorizationServerConfigurerAdapter,间接继承了AuthorizationServerConfigurer。 看到AuthorizationServerConfigurer ,可以想象应该有个xxxConfiguration的配置文件来解析他。是的,他就是被**AuthorizationServerSecurityConfiguration**
搜集解析的. 而AuthorizationServerSecurityConfiguration
会被WebSecurityConfiguration
配置类搜集。这样我们自定义的配置就这样跟Security配置体系关联起来了
public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private List<AuthorizationServerConfigurer> configurers = Collections.emptyList(); }
资源服务器配置:
EnableResourceServer注解:主要是激活ResourceServerConfiguration类
- ResourceServerConfiguration : 也间接实现了SecurityConfigurer接口,也就是说也会被WebSecurityConfiguration配置类搜集解析。
MyResourceServerConfiguration: 自定义配置。实现了ResourceServerConfigurer
接口,他会被ResourceServerConfiguration
配置类搜集解析,最终也是会进入WebSecurityConfiguration
配置类
到此我们可以看出,集成Oauth2时,看似多出的两个配置类,其实还是在间接配置Security ,最终都会在Security框架体系内生效。 也说明了Oauth2框架就是架设在Security框架之上。
4.Security Oauth2 流程图
token获取流程图
接口请求流程图
5.总结
- Spring security 在web应用中是基于Filter的
- Spring security Oauth2 基于 Security 框架添加认证模式的逻辑