文章目录
学习参考
Spring Security框架配置运行流程完整分析 - 【必看】
OAuth2授权流程和源码解析 - 自己总结 - 有道笔记~
@EnableAuthorizationServer授权服务注解源码分析
@EnableResourceServer资源服务注解源码分析
Security OAuth2 SSO单点登录源码剖析 - 自己总结 - 有道笔记~
(八)SpringCloud+Security+Oauth2–token增强个性化和格式化输出
十七.SpringCloud+Security+Oauth2实现微服务授权 -非对称加密生成JWT令牌
十四.SpringCloud+Security+Oauth2实现微服务授权 - 网关统一鉴权
十三.SpringCloud+Security+Oauth2实现微服务授权 - 服务之间授权
@EnableResourceServer
概要
@EnableResourceServer用于开启资源服务器,它使用@Import注解引入了ResourceServerConfiguration
- ResourceServerConfiguration
资源服务器配置类
,继承自继承自WebSecurityConfigurerAdapter,order为3,大于AuthorizationServerSecurityConfiguration的order,其中WebSecurityConfigurerAdapter的order默认为100;由此可见它们的顺序:AuthorizationServerSecurityConfiguration、ResourceServerConfiguration、WebSecurityConfigurerAdapter- 自动注入容器中定义的TokenStore令牌存储组件、所有的tokenServices资源服务器令牌服务、AuthorizationServerEndpointsConfiguration授权服务器端点配置类、所有的
ResourceServerConfigurer
资源服务器配置器 - 在配置HttpSecurity时,会创建1个
ResourceServerSecurityConfigurer
资源服务器安全配置器,它用于添加到HttpSecurity中继续配置HttpSecurity,会往HttpSecurity中添加OAuth2AuthenticationProcessingFilter
这一重要的过滤器,该OAuth2AuthenticationProcessingFilter用于使用令牌加载OAuth2Authenticaiton认证对象,绑定到当前线程中。 - 在配置HttpSecurity时,也会对把自动注入的TokenStore、tokenServices设置到ResourceServerSecurityConfigurer资源服务器安全配置器中,并且会使用ResourceServerConfigurer资源服务器配置器对该ResourceServerSecurityConfigurer资源服务器安全配置器进行配置
- 可定义ResourceServerConfigurer类型的bean来配置ResourceServerSecurityConfigurer和HttpSecurity,它们的配置过程都在ResourceServerConfiguration的configure(HttpSecurity)方法中
ResourceServerConfiguration
资源服务器配置类
,继承自继承自WebSecurityConfigurerAdapter,order为3
属性定义
/* 尝试注入容器中的 TokenStore 令牌存储器 */
@Autowired(required = false)
private TokenStore tokenStore;
/* 尝试注入容器中的 AuthenticationEventPublisher 认证事件发布器 */
@Autowired(required = false)
private AuthenticationEventPublisher eventPublisher;
/* 尝试注入容器中的 ResourceServerTokenServices 资源服务器令牌服务 */
@Autowired(required = false)
private Map<String, ResourceServerTokenServices> tokenServices;
/* 注入 ApplicationContext 应用上下文 */
@Autowired
private ApplicationContext context;
/* 注入容器中的所有定义的 ResourceServerConfigurer 资源服务器配置器 */
private List<ResourceServerConfigurer> configurers = Collections.emptyList();
/* 尝试注入容器中的 AuthorizationServerEndpointsConfiguration 授权服务器都单点配置类
因为: 授权服务器经常也会开启资源服务器的功能, 如果未开启, 这里就会是null */
@Autowired(required = false)
private AuthorizationServerEndpointsConfiguration endpoints;
configure(HttpSecurity)
其实就是在配置ResourceServerSecurityConfigurer 资源服务器安全配置器,而 资源服务器安全配置器 则是为了给HttpSecurity添加1个OAuth2AuthenticationProcessingFilter过滤器
@Override
protected void configure(HttpSecurity http) throws Exception {
// 创建1个【ResourceServerSecurityConfigurer 资源服务器安全配置器】, 用来继续配置HttpSecurity
ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer();
// 解析出1个 资源服务器令牌服务,
// 从自动注入的tokenServices中获取, 如果没有, 则返回null; 如果只有1个, 则选择这个; 如果有多个, 须使用@Primary来标注使用哪个
ResourceServerTokenServices services = resolveTokenServices();
// 如果解析出来了 ResourceServerTokenServices 资源服务器令牌服务, 则设置给 资源服务器安全配置器 的 resourceTokenServices属性
if (services != null) {
resources.tokenServices(services);
}
else {
// 此时, 容器中未定义任何 ResourceServerTokenServices 资源服务器令牌服务
if (tokenStore != null) {
// 如果令牌存储器有定义 TokenStore令牌存储器,
// 则将此TokenStore设置给 【ResourceServerSecurityConfigurer 资源服务器安全配置器】的tokenStore属性
resources.tokenStore(tokenStore);
}
else if (endpoints != null) {
// 此时容器中未定义任何 TokenStore令牌存储器, 则使用AuthorizationServerEndpointsConfiguration的endpoints的tokenStore属性
// 设置给【ResourceServerSecurityConfigurer 资源服务器安全配置器】的tokenStore属性
resources.tokenStore(endpoints.getEndpointsConfigurer().getTokenStore());
}
}
// 如果容器中有定义 AuthenticationEventPublisher 认证事件发布器, 则将它设置给【ResourceServerSecurityConfigurer 资源服务器安全配置器】
if (eventPublisher != null) {
resources.eventPublisher(eventPublisher);
}
// 使用容器中所有定义的 *ResourceServerConfigurer* 资源服务器配置器 来配置【ResourceServerSecurityConfigurer 资源服务器安全配置器】
// 这个扩展点非常重要, 注意它的执行时机哦~
for (ResourceServerConfigurer configurer : configurers) {
configurer.configure(resources);
}
// 给HttpSecurity添加一些配置器
http
// 1. 给HttpSecurity的sharedObjects属性中AuthenticationManagerBuilder类型的公共属性添加AnonymousAuthenticationProvider
// 2. 这个AuthenticationManagerBuilder的公共属性, 在 WebSecurityConfigurerAdapter 的 getHttp()方法中, 在创建HttpSecurity时,
// 就会把WebSecurityConfigurerAdapter的authenticationBuilder属性作为AuthenticationManagerBuilder传进去作为HttpSecurity的公共属性
.authenticationProvider(new AnonymousAuthenticationProvider("default"))
// 添加异常处理配置器, 将resources的accessDeniedHandler设置给异常处理配置器
.exceptionHandling().accessDeniedHandler(resources.getAccessDeniedHandler())
.and()
// 添加会话管理配置器, 不使用HttpSession
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// 移除掉csrf配置器
.csrf().disable();
// 将【ResourceServerSecurityConfigurer 资源服务器安全配置器】添加到HttpSecurity,
// 目的是使用刚刚配置好的 资源服务器安全配置器 来配置HttpSecurity
http.apply(resources);
// 1. 如果 AuthorizationServerEndpointsConfiguration 不为空, 那说明开启了授权服务器
// 那就把@FrameworkEndpoint标注的处理器方法的请求路径排除在 资源服务过滤器链 之外
// 2. 其实根本就不用吧?!当同时开启授权服务和资源服务的话, 授权服务过滤器链肯定排在资源服务器链前面, 那么肯定先匹配上授权服务过滤器链阿,
// 这在AuthorizationServerSecurityConfiguration的configure(HttpSecurity)方法中就设置了资源过滤器链的匹配的路径,
// 难道这里只是想说 授权服务的请求不归资源服务器链管?
if (endpoints != null) {
http.requestMatcher(new NotOAuthRequestMatcher(endpoints.oauth2EndpointHandlerMapping()));
}
// 使用容器中所有定义的 *ResourceServerConfigurer* 资源服务器配置器 来配置HttpSecurity过滤器链
// 这个扩展点非常重要, 注意它的执行时机哦~
for (ResourceServerConfigurer configurer : configurers) {
configurer.configure(http);
}
// 如果没有定义 ResourceServerConfigurer 资源服务器配置器, 那就对资源服务过滤器链匹配到的请求 都需要认证后,才能访问
if (configurers.isEmpty()) {
http.authorizeRequests().anyRequest().authenticated();
}
}
ResourceServerSecurityConfigurer
资源服务器安全配置器,用于给HttpSecurity添加OAuth2AuthenticationProcessingFilter过滤器,该过滤器会通过读取令牌加载OAuth2Authentication绑定到当前线程,以确认客户端身份和用户授权信息。
// 异常处理过滤器的认证入口点
AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
// 异常处理过滤器的访问拒绝处理器
AccessDeniedHandler accessDeniedHandler = new OAuth2AccessDeniedHandler();
// 【OAuth2AuthenticationProcessingFilter 资源服务器过滤器】
// 它会使用 TokenExtractor 令牌提取器 从当前请求中获取 令牌, 然后把令牌交给 OAuth2AuthenticationManager 作认证, OAuth2AuthenticationManager 又会交给 ResourceServerTokenServices 来加载 OAuth2Authentication 认证对象
OAuth2AuthenticationProcessingFilter resourcesServerFilter;
// 认证管理器, 实现一般为: OAuth2AuthenticationManager, 它会持有ResourceServerTokenServices, 用于读取令牌
AuthenticationManager authenticationManager;
// 认证事件发布器
AuthenticationEventPublisher eventPublisher = null;
// 资源服务器令牌服务, 用于读取令牌, 以获得OAuth2Authentication认证对象
ResourceServerTokenServices resourceTokenServices;
// 令牌存储器, 用户存储令牌和查询令牌
TokenStore tokenStore = new InMemoryTokenStore();
// 资源服务标识, 用于标识此资源服务。同时, ClientDetails客户端必须拥有该资源服务, 才能访问该资源
String resourceId = "oauth2-resource";
// 用于支持权限表达式, 可参考: Security方法注解权限控制过程及自定义权限表达式 https://blog.csdn.net/qq_16992475/article/details/130462486
SecurityExpressionHandler<FilterInvocation> expressionHandler = new OAuth2WebSecurityExpressionHandler();
// 令牌提取器, 用于在 OAuth2AuthenticationProcessingFilter 过滤器中, 从请求中获取令牌
TokenExtractor tokenExtractor;
// 获取认证详情信息
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
// 是否无状态。
// 如果是false, 则不仅仅是OAuth2客户端可以访问, 非OAuth2客户端也可以访问;
// 如果是true, 则如果是非OAuth2客户端已被认证了, 那么在OAuth2AuthenticationProcessingFilter中会清除掉这个认证对象
//(注意OAuth2AuthenticationProcessingFilter过滤器排在UsernamePasswordAuthenticationFilter前面, 这在FilterComparator和ResourceServerSecurityConfigurer的configure(HttpSecurity)方法中可以看到)
boolean stateless = true;
init(HttpSecurit)
@Override
public void init(HttpSecurity http) {
// 1. 其实就是给HttpSecurity过滤器链上的异常过滤器设置入口点属性,
// 2. 将当前ResourceServerSecurityConfigurer资源服务器安全配置器的authenticationEntryPoint属性设置到ExceptionHandlingConfigurer的defaultEntryPointMappings中, 并以MediaTypeRequestMatcher实例作为key
registerDefaultAuthenticationEntryPoint(http);
}
configure(HttpSecurity)
@Override
public void configure(HttpSecurity http) {
// 创建1个 OAuth2AuthenticationManager OAuth2认证管理器, 并完成 OAuth2认证管理器 的配置
AuthenticationManager oauthAuthenticationManager = oauthAuthenticationManager(http);
// 创建1个 【OAuth2AuthenticationProcessingFilter 过滤器】
resourcesServerFilter = new OAuth2AuthenticationProcessingFilter();
// 给 【OAuth2AuthenticationProcessingFilter 过滤器】 设置 OAuth2认证管理器 和 认证入口点
resourcesServerFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager);
// 【OAuth2AuthenticationProcessingFilter 过滤器】 设置 认证事件发布器
if (eventPublisher != null) {
resourcesServerFilter.setAuthenticationEventPublisher(eventPublisher);
}
// 给 【OAuth2AuthenticationProcessingFilter 过滤器】 设置 令牌提取器
if (tokenExtractor != null) {
resourcesServerFilter.setTokenExtractor(tokenExtractor);
}
// 给 【OAuth2AuthenticationProcessingFilter 过滤器】 设置 认证详情源
if (authenticationDetailsSource != null) {
resourcesServerFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
}
// 使用ObjectPostProcessor处理 【OAuth2AuthenticationProcessingFilter 过滤器】
resourcesServerFilter = postProcess(resourcesServerFilter);
// 给 【OAuth2AuthenticationProcessingFilter 过滤器】 设置 stateless
resourcesServerFilter.setStateless(stateless);
http
// 添加FilterSecurityInterceptor过滤器, 并添加支持权限表达式处理器
.authorizeRequests().expressionHandler(expressionHandler)
.and()
// 添加【OAuth2AuthenticationProcessingFilter 过滤器】, 放置在AbstractPreAuthenticatedProcessingFilter前面
// 在我们熟悉的 UsernamePasswordAuthenticationFilter, 可在FilterComparator中看到
.addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class)
// 添加异常处理配置器, 并指定 访问拒绝处理器 和 认证入口点
.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(authenticationEntryPoint);
}
private AuthenticationManager oauthAuthenticationManager(HttpSecurity http) {
// 创建1个 OAuth2AuthenticationManager OAuth2认证管理器
OAuth2AuthenticationManager oauthAuthenticationManager = new OAuth2AuthenticationManager();
// 如果已经设置认证管理器, 并且 如果认证管理器是 OAuth2AuthenticationManager 那么给该oauthAuthenticationManager继续设置属性,
// 如果认证管理器不是 OAuth2AuthenticationManager 类型, 那么直接返回
if (authenticationManager != null) {
if (authenticationManager instanceof OAuth2AuthenticationManager) {
oauthAuthenticationManager = (OAuth2AuthenticationManager) authenticationManager;
}
else {
return authenticationManager;
}
}
// 设置 资源服务标识, 若客户端无该资源服务标识, 将不能访问该资源
oauthAuthenticationManager.setResourceId(resourceId);
// 获取 资源服务器令牌服务, 并设置给 OAuth2认证管理器
oauthAuthenticationManager.setTokenServices(resourceTokenServices(http));
// 获取 客户端服务, 并设置给 OAuth2认证管理器
oauthAuthenticationManager.setClientDetailsService(clientDetails());
// 返回配置好的 OAuth2认证管理器
return oauthAuthenticationManager;
}
// 获取资源服务器令牌服务
private ResourceServerTokenServices resourceTokenServices(HttpSecurity http) {
// 获取 ResourceServerSecurityConfigurer 设置的 resourceTokenServices 或 创建1个DefaultTokenServices 作为 资源服务器令牌服务
tokenServices(http);
return this.resourceTokenServices;
}
// 获取 资源服务器令牌服务
private ResourceServerTokenServices tokenServices(HttpSecurity http) {
// 如果设置 给 ResourceServerSecurityConfigurer 设置了 resourceTokenServices, 就直接返回指定的资源服务器令牌服务
if (resourceTokenServices != null) {
return resourceTokenServices;
}
// 未给 ResourceServerSecurityConfigurer 设置 令牌服务 属性时,
// 就会创建默认的令牌服务, 并使用 ResourceServerSecurityConfigurer 设置 的令牌存储器
// 创建1个默认的令牌服务
DefaultTokenServices tokenServices = new DefaultTokenServices();
// 获取tokenStore 设置到 令牌服务 中()
tokenServices.setTokenStore(tokenStore());
// 设置支持刷新令牌
tokenServices.setSupportRefreshToken(true);
// 设置客户端详情服务
tokenServices.setClientDetailsService(clientDetails());
// DefaultTokenServices可作为资源服务器令牌服务
this.resourceTokenServices = tokenServices;
return tokenServices;
}
// 获取 令牌存储器
// 直接返回 ResourceServerSecurityConfigurer 的 tokenStore 属性, 显然它一定要被设置, 否则一调用就会抛异常
private TokenStore tokenStore() {
Assert.state(tokenStore != null, "TokenStore cannot be null");
return this.tokenStore;
}
// 获取访问拒绝处理器, 直接返回 ResourceServerSecurityConfigurer 的 accessDeniedHandler 属性
public AccessDeniedHandler getAccessDeniedHandler() {
return this.accessDeniedHandler;
}