文章目录
- 学习参考
- @EnableAuthorizationServer
- AuthorizationServerEndpointsConfiguration
- TokenKeyEndpointRegistrar
- ==endpoints==&clientDetailsService&configurers
- AuthorizationEndpoint授权端点
- TokenEndpoint令牌端点
- CheckTokenEndpoint校验令牌端点
- WhitelabelApprovalEndpoint授权页端点
- WhitelabelErrorEndpoint授权错误页端点
- FrameworkEndpointHandlerMapping
- ConsumerTokenServices撤销令牌服务
- ==AuthorizationServerTokenServices授权服务器令牌服务==
- AuthorizationServerSecurityConfiguration
学习参考
Spring Security框架配置运行流程完整分析 - 【必看】
OAuth2授权流程和源码解析 - 自己总结 - 有道笔记~
@EnableAuthorizationServer授权服务注解源码分析
@EnableResourceServer资源服务注解源码分析
Security OAuth2 SSO单点登录源码剖析 - 自己总结 - 有道笔记~
(八)SpringCloud+Security+Oauth2–token增强个性化和格式化输出
十七.SpringCloud+Security+Oauth2实现微服务授权 -非对称加密生成JWT令牌
十四.SpringCloud+Security+Oauth2实现微服务授权 - 网关统一鉴权
十三.SpringCloud+Security+Oauth2实现微服务授权 - 服务之间授权
@EnableAuthorizationServer
@EnableAuthorizationServer用于开启授权服务器,它使用@Import注解引入了如下2个配置类:
-
AuthorizationServerEndpointsConfiguration
- 授权服务器端点配置类;
- 用于引入授权端点、令牌端点、校验令牌端点、授权页面端点、FrameworkEndpoint端点处理器映射器、授权服务器令牌服务、撤销令牌服务、TokenKey令牌密钥端点等组件;
- 内部创建了1个AuthorizationServerEndpointsConfigurer作为成员变量endpoints,并且会注入容器中所有的AuthorizationServerConfigurer来对该endpoints成员变量进行配置,并且注入容器中的clientDetailsService设置到endpoints中。
- 当前AuthorizationServerEndpointsConfiguration配置类中的定义的很多端点组件所用到的其它组件都是直接从endpoints成员变量中拿的。
-
AuthorizationServerSecurityConfiguration
- 授权服务器安全配置类,继承自WebSecurityConfigurerAdapter,order为0,其中WebSecurityConfigurerAdapter的order默认为100;重写了父类WebSecurityConfigurerAdapter的configure(AuthenticationManagerBuilder)方法,使得disableLocalConfigureAuthenticationBldr属性为false,即不会从spring容器中获取全局的认证管理器,而是使用localConfigureAuthenticationBldr来构建认证管理器作为ProviderManager的parent属性;
- 使用@Import引入了ClientDetailsServiceConfiguration客户端服务配置类和AuthorizationServerEndpointsConfiguration授权服务器端点配置类,所以
授权服务器安全配置类
默认就引入了授权服务器端点配置类
; - 引入的ClientDetailsServiceConfiguration客户端服务配置类中定义了1个ClientDetailsService的bean;
- 自动注入容器中所有的AuthorizationServerConfigurer,并对自动注入的ClientDetailsService的bean进行配置;
- 在重写的configure(HttpSecurity)方法中创建了1个AuthorizationServerSecurityConfigurer授权服务器安全配置器,使用自动注入的所有AuthorizationServerConfigurer对该创建的AuthorizationServerSecurityConfigurer授权服务器安全配置器进行配置,并将此权服务器安全配置器添加到HttpSecurity中;这个AuthorizationServerSecurityConfigurer授权服务器安全配置器的作用就是用来配置HttpSecurity的。如果AuthorizationServerEndpointsConfiguration授权服务器端点配置类中的endpoints属性(即AuthorizationServerEndpointsConfigurer配置器)的userDetailsServiceOverride为false(即userDetailsService还未具体设置),那就默认从HttpSecurity的sharedObjects中获取UserDetailsService类型对应的值设置到uthorizationServerEndpointsConfiguration授权服务器端点配置类中的endpoints属性的userDetailsService属性中;为HttpSecurity添加配置类,配置tokenEndpointPath、tokenKeyPath、checkTokenPath路径访问权限和该HttpSecurity仅匹配此3个路径的请求;关闭会话配置类;将自动注入的clientDetailsService添加到HtppSecurity的sharedObjects属性中。
AuthorizationServerEndpointsConfiguration
授权服务器端点配置类,主要用来配置端点组件
TokenKeyEndpointRegistrar
AuthorizationServerEndpointsConfiguration中使用了@Import注解引入了TokenKeyEndpointRegistrar,这个TokenKeyEndpointRegistrar会寻找容器中的JwtAccessTokenConverter类型的bean,找到第一个JwtAccessTokenConverter类型的bean作为TokenKeyEndpoint的构造方法的参数,于是就注册了处理/oauth/token_key
令牌公钥的端点
@Configuration
@Import(TokenKeyEndpointRegistrar.class) // 使用@Import注解引入了TokenKeyEndpointRegistrar
public class AuthorizationServerEndpointsConfiguration {
TokenKeyEndpointRegistrar实现了BeanDefinitionRegistryPostProcessor
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 找到第一个JwtAccessTokenConverter类型的bean作为TokenKeyEndpoint的构造方法的参数
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory,
JwtAccessTokenConverter.class,
false, false);
if (names.length > 0) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(TokenKeyEndpoint.class);
builder.addConstructorArgReference(names[0]);
registry.registerBeanDefinition(TokenKeyEndpoint.class.getName(), builder.getBeanDefinition());
}
}
endpoints&clientDetailsService&configurers
创建了1个AuthorizationServerEndpointsConfigurer授权服务器端点配置器
,它内部维护了很多端点组件需要的属性,需要通过容器中注入的AuthorizationServerConfigurer授权服务器配置器
来对授权服务器端点配置器
进行设置,这样后面当前AuthorizationServerEndpointsConfiguration类中定义的各个端点组件就可以从授权服务器配置器中拿到需要的属性来完成端点的配置了。
// 授权服务器端点配置器 作为 成员变量 endpoints
private AuthorizationServerEndpointsConfigurer endpoints = new AuthorizationServerEndpointsConfigurer();
// 1. 注入容器中的 ClientDetailsService, 它定义在ClientDetailsServiceConfiguration
// 2. 由@EnableAuthorizationServer注解引入的AuthorizationServerSecurityConfiguration类使用@Import导入
@Autowired
private ClientDetailsService clientDetailsService;
// 1. 注入容器中的所有授权服务器配置器
// 2. 一般来说, 我们就喜欢在使用@EnableAuthorizationServer注解所标注的类继承自AuthorizationServerConfigurerAdapter
@Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
// 初始化方法
@PostConstruct
public void init() {
// 1. 遍历所有的 AuthorizationServerConfigurer *授权服务器配置器* - 扩展点,
// 2. 使用这些配置器来配置 AuthorizationServerEndpointsConfiguration类中定义的成员变量 endpoints
// 3. endpoints的类型为 AuthorizationServerEndpointsConfigurer 授权服务器端点配置器
for (AuthorizationServerConfigurer configurer : configurers) {
try {
configurer.configure(endpoints);
} catch (Exception e) {
throw new IllegalStateException("Cannot configure enpdoints", e);
}
}
// 将注入的clientDetailsService设置给 endpoints授权服务器端点配置器
endpoints.setClientDetailsService(clientDetailsService);
}
AuthorizationServerEndpointsConfigurer
作为AuthorizationServerEndpointsConfiguration配置类中的endpoints属性
// 授权服务器令牌服务, 用于颁发令牌
AuthorizationServerTokenServices tokenServices;
// 撤销令牌服务, 用于撤销令牌
ConsumerTokenServices consumerTokenServices;
// 授权码服务, 用于授权码流程生成授权码
AuthorizationCodeServices authorizationCodeServices;
// 资源服务器令牌服务, 用于读取令牌获取令牌所关联的用户授权信息, 可以是从远程获取, 也可以是读取jwt令牌
ResourceServerTokenServices resourceTokenServices;
// 令牌存储器, 用于存储令牌
TokenStore tokenStore;
// 令牌增强器, 对令牌服务所颁发的令牌进行增强, 说简单点就是可以加一些额外的数据到令牌中
TokenEnhancer tokenEnhancer;
// 访问令牌转换器, 将令牌对象和OAuth2认证对象与Map之间进行转换
AccessTokenConverter accessTokenConverter;
// 用户对客户端的scope授权的存储器
ApprovalStore approvalStore;
// 令牌授权器, 有5种不同的令牌授权器, 根据所选择的授权模式匹配的授权器处理授权
// - AuthorizationCodeTokenGranter、
// - ImplicitTokenGranter、
// - ResourceOwnerPasswordTokenGranter、
// - ClientCredentialsTokenGranter、
// - RefreshTokenGranter
TokenGranter tokenGranter;
// OAuth2请求工厂, 用于管理: AuthorizationRequest-授权请求、TokenRequest-令牌请求、OAuth2Request-OAuth2请求
// 这个工厂就是为了创建OAuth2请求对象的, 只不过创建的方式有如下2种:
// 第一种: 根据授权请求参数得到授权请求对象, 再使用授权请求对象得到OAuth2请求对象
// authorizationParameters授权请求参数 -> AuthorizationRequest授权请求对象
// AuthorizationRequest授权请求对象 -> OAuth2Request OAuth2请求
// 第二种: 先得到令牌请求对象, 然后令牌请求对象+clientDetails, 就得到了OAuth2请求对象
// TokenRequest令牌请求对象 + clientDetails -> OAuth2Request OAuth2请求
// requestParameters + ClientDetails -> TokenRequest令牌请求对象
// AuthorizationRequest授权请求对象 + grantType -> TokenRequest令牌请求对象
OAuth2RequestFactory requestFactory;
// 校验授权请求的scope列表, 在clientDetails中都支持
OAuth2RequestValidator requestValidator;
// 客户端发起的认证请求是否被当前用户所同意, 可用于跳过用户在授权页授权的过程, 即自动授权
UserApprovalHandler userApprovalHandler;
// 认证管理器, 此属性不为空时, 才会开启支持密码模式。因为能够根据用户名查询出用户, 然后匹配密码嘛
AuthenticationManager authenticationManager;
// 客户端服务, 能够根据clientId查询出客户端详情
ClientDetailsService clientDetailsService;
// servlet的路径前缀, 不填就行, dispatcherServlet默认就是/
String prefix;
// 用于修改/oauth/confirm_access授权页面路径和/oauth/error授权错误页面路径
Map<String, String> patternMap = new HashMap<String, String>();
// 请求令牌的接口允许使用的请求方式, 默认支持POST, 再加个GET
Set<HttpMethod> allowedTokenEndpointRequestMethods = new HashSet<HttpMethod>();
// 处理器映射器, 用来扫描容器中的@FrameworkEndpoint注解的端点
FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping;
// 禁用approvalStore
boolean approvalStoreDisabled;
// 拦截器, 须实现HandlerInterceptor或WebRequestInterceptor接口,
// 添加到FrameworkEndpointHandlerMapping处理器映射器中
List<Object> interceptors = new ArrayList<Object>();
// 默认令牌服务,
// 它实现了: AuthorizationServerTokenServices、
// ResourceServerTokenServices、
// ConsumerTokenServices
// 这3个接口,
// 主要包含: TokenStore 令牌存储、TokenEnhancer 令牌增强、ClientDetailsService 客户端服务、认证管理器
//
DefaultTokenServices defaultTokenServices;
// 在创建DefaultTokenServices默认令牌服务时, 作为令牌服务的authenticationManager属性,
// 即ProviderManager的AuthenticationProvider列表之一的PreAuthenticatedAuthenticationProvider的内部实现, 只不过还被UserDetailsByNameServiceWrapper包了一下
UserDetailsService userDetailsService;
// 是否设置了tokenServices
boolean tokenServicesOverride = false;
// 是否设置了userDetailsService
boolean userDetailsServiceOverride = false;
// 刷新令牌是否可重用
boolean reuseRefreshToken = true;
// 用于授权端点、令牌端点、校验令牌端点处理异常, 默认实现为: DefaultWebResponseExceptionTranslator
WebResponseExceptionTranslator<OAuth2Exception> exceptionTranslator;
// 用户请求授权时, 所携带的重定向uri与客户端注册的重定向uri确定重定向地址, 默认实现为: DefaultRedirectResolver
RedirectResolver redirectResolver;
AuthorizationEndpoint授权端点
授权端点用来处理授权请求处理,用户发起对指定客户端的授权,跳转到授权页面,随后,用户同意该授权,发起授权请求,之后由授权模式所指定的类型的授权器处理授权请求。
@Bean
public AuthorizationEndpoint authorizationEndpoint() throws Exception {
// 创建1个授权端点组件
AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();
// 1. 这个getEndpointsConfigurer()就是去拿AuthorizationServerEndpointsConfiguration的endpoints属性的frameworkEndpointHandlerMapping属性
// 2. 这个getEndpointsConfigurer()会确保endpoints属性的defaultTokenServices不为null, 否则会创建1个新的DefaultTokenServices作为令牌服务
// 3. 这里的getFrameworkEndpointHandlerMapping()方法会确保endpoints的frameworkEndpointHandlerMapping不为null,否则会创建1个新的FrameworkEndpointHandlerMapping
FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
// 设置用户授权页面转发路径, 默认为: forward:/oauth/confirm_access
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
// 从endpoints中去拿exceptionTranslator属性, 用来处理授权处理过程中的异常
authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator());
// 设置用户拒绝授权页面转发路径, 默认为: forward:/oauth/error
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
// 从endpoints中去拿【tokenGranter授权器属性】, 用来支持不同授权模式的授权, 能支持哪些授权类型就是看这个属性
// 会确保该授权器不为空, 否则会创建1个组合式的授权器
authorizationEndpoint.setTokenGranter(tokenGranter());
// 设置客户端信息服务
authorizationEndpoint.setClientDetailsService(clientDetailsService);
// 从endpoints中去拿authorizationCodeServices服务, 会确保该服务不为空, 否则创建1个InMemoryAuthorizationCodeServices
authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices());
// 从endpoints中去拿requestFactory, 会确保该服务不为空, 否则创建1个DefaultOAuth2RequestFactory
authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
// 从endpoints中去拿requestValidator, 会确保该服务不为空, 否则创建1个DefaultOAuth2RequestValidator
authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
// 从endpoints中去拿userApprovalHandler, 会确保该服务不为空, 否则根据tokenStore的类型创建1个ApprovalStoreUserApprovalHandler或者TokenStoreUserApprovalHandler
authorizationEndpoint.setUserApprovalHandler(userApprovalHandler());
// 从endpoints中去拿redirectResolver, 会确保该服务不为空, 否则创建1个DefaultRedirectResolver
authorizationEndpoint.setRedirectResolver(redirectResolver());
return authorizationEndpoint;
}
TokenEndpoint令牌端点
令牌端点用来处理颁发令牌的请求,当客户端根据授权模式发起获取令牌的请求时,由该端点处理
@Bean
public TokenEndpoint tokenEndpoint() throws Exception {
// 创建令牌端点组件
TokenEndpoint tokenEndpoint = new TokenEndpoint();
// 设置客户端信息服务
tokenEndpoint.setClientDetailsService(clientDetailsService);
// 从endpoints中去拿exceptionTranslator属性, 用来处理授权处理过程中的异常
tokenEndpoint.setProviderExceptionHandler(exceptionTranslator());
// 从endpoints中去拿【tokenGranter授权器属性】, 用来支持不同授权模式的授权, 能支持哪些授权类型就是看这个属性
// 会确保该授权器不为空, 否则会创建1个组合式的授权器
tokenEndpoint.setTokenGranter(tokenGranter());
// 从endpoints中去拿requestFactory, 会确保该服务不为空, 否则创建1个DefaultOAuth2RequestFactory
tokenEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
// 从endpoints中去拿requestValidator, 会确保该服务不为空, 否则创建1个DefaultOAuth2RequestValidator
tokenEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
// 从endpoints中去拿allowedTokenEndpointRequestMethods属性, 默认只支持post请求方式
tokenEndpoint.setAllowedRequestMethods(allowedTokenEndpointRequestMethods());
return tokenEndpoint;
}
CheckTokenEndpoint校验令牌端点
当客户端拿到令牌之后,将此令牌交给资源服务器,资源服务器可拿此令牌向授权服务器校验此令牌;或者客户端可直接将令牌传给授权服务器校验令牌;校验令牌时会返回该令牌所关联的用户授权信息。
@Bean
public CheckTokenEndpoint checkTokenEndpoint() {
// 从endpoints中去拿resourceTokenServices, 先去检查tokenServices是否为ResourceServerTokenServices,
// 如果是, 则优先返回tokenServices, 否则判断resourceTokenServices是否为空, 若为空, 则创建1个DefaultTokenServices
CheckTokenEndpoint endpoint = new CheckTokenEndpoint(getEndpointsConfigurer().getResourceServerTokenServices());
// 从endpoints中去拿accessTokenConverter, 会确保该服务不为空, 否则创建1个DefaultAccessTokenConverter
endpoint.setAccessTokenConverter(getEndpointsConfigurer().getAccessTokenConverter());
// 从endpoints中去拿exceptionTranslator属性, 用来处理授权处理过程中的异常
endpoint.setExceptionTranslator(exceptionTranslator());
return endpoint;
}
WhitelabelApprovalEndpoint授权页端点
授权页面端点,用来展示授权页面的逻辑,用户发起对指定客户端的授权后,会跳转到授权页面,就是该端点控制的,所看到的授权页面的内容的html的代码都写在WhitelabelApprovalEndpoint类中
@Bean
public WhitelabelApprovalEndpoint whitelabelApprovalEndpoint() {
return new WhitelabelApprovalEndpoint();
}
WhitelabelErrorEndpoint授权错误页端点
授权错误页面端点,当用户进入到授权页面,然后点击拒绝,将会由该端点处理,所看到的页面的内容的html的代码都写在WhitelabelErrorEndpoint类中
@Bean
public WhitelabelErrorEndpoint whitelabelErrorEndpoint() {
return new WhitelabelErrorEndpoint();
}
FrameworkEndpointHandlerMapping
springmvc的中处理器映射器组件,它继承自RequestMappingHandlerMapping,作用就是扫描容器中带有@FrameworkEndpoint注解的bean,显然,上述所有的端点都带有该注解。
根据请求路径匹配到对应的端点,交给端点中的处理器方法处理。
@Bean
public FrameworkEndpointHandlerMapping oauth2EndpointHandlerMapping() throws Exception {
return getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
}
ConsumerTokenServices撤销令牌服务
用来撤销令牌,内部只定义了1个撤销令牌的方法,撤销的实现最终是委托给了TokenStore令牌存储器,既然是他存储的,那当然由他来撤销了。
@Bean
public FactoryBean<ConsumerTokenServices> consumerTokenServices() {
return new AbstractFactoryBean<ConsumerTokenServices>() {
@Override
public Class<?> getObjectType() {
return ConsumerTokenServices.class;
}
@Override
protected ConsumerTokenServices createInstance() throws Exception {
// 从endpoints中去拿consumerTokenServices, 先去检查tokenServices是否为ConsumerTokenServices,
// 如果是, 则优先返回tokenServices, 否则判断consumerTokenServices是否为空, 若为空, 则创建1个DefaultTokenServices
return getEndpointsConfigurer().getConsumerTokenServices();
}
};
}
AuthorizationServerTokenServices授权服务器令牌服务
@Bean
public FactoryBean<AuthorizationServerTokenServices> defaultAuthorizationServerTokenServices() {
// 从endpoints中去拿defaultTokenServices, 会确保该服务不为空, 否则创建1个DefaultTokenServices
return new AuthorizationServerTokenServicesFactoryBean(endpoints);
}
AuthorizationServerSecurityConfiguration
授权服务器安全配置类,主要用来配置保护与授权相关的端点的过滤器链。继承自WebSecurityConfigurerAdapter。
@Configuration
@Order(0) // 继承自 WebSecurityConfigurerAdapter, order为0, 说明创建得到的HttpSecurity比较靠前, 匹配优先级高
@Import({ ClientDetailsServiceConfiguration.class, // 引入了客户端配置类
AuthorizationServerEndpointsConfiguration.class }) // 引入了授权服务器端点配置类
public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
属性定义如下
// 1. 注入容器中的所有授权服务器配置器
// 2. 一般来说, 我们就喜欢在使用@EnableAuthorizationServer注解所标注的类继承自AuthorizationServerConfigurerAdapter
@Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
// 1. 注入容器中的 ClientDetailsService, 它定义在ClientDetailsServiceConfiguration
// 2. 由@EnableAuthorizationServer注解引入的AuthorizationServerSecurityConfiguration类使用@Import导入
@Autowired
private ClientDetailsService clientDetailsService;
// 注入授权服务器端点配置类, 这样可以拿到endpoints里的endpoints属性~
@Autowired
private AuthorizationServerEndpointsConfiguration endpoints;
// 自动注入容器中的ClientDetailsServiceConfigurer客户端服务配置器, 它就定义在ClientDetailsServiceConfiguration类中
@Autowired
public void configure(ClientDetailsServiceConfigurer clientDetails) throws Exception {
// 1. 遍历所有的 AuthorizationServerConfigurer *授权服务器配置器* - 扩展点,
// 2. 使用这些配置器来配置 AuthorizationServerConfigurer
for (AuthorizationServerConfigurer configurer : configurers) {
configurer.configure(clientDetails);
}
}
configure(AuthenticationManagerBuilder)
// 1. 是个空方法, 只是覆盖了定义在父类WebSecurityConfigurerAdapter的configure(AuthenticationManagerBuilder)方法
// 2. 这样在创建认证管理器的时候, 就不会从全局中查找认证管理器(即不会使用AuthenticationConfiguration获取认证管理器的逻辑), 而是使用WebSecurityConfigurerAdapter的localConfigureAuthenticationBldr属性来创建authenticationManager作为最终构建的ProviderManager的parent属性,
// 3. 最终的构建的ProviderManager是由WebSecurityConfigurerAdapter的authenticationBuilder属性构建的, 并将此ProviderManager作为AuthenticationManager类型存放到HttpSecurity的shareObjects属性中
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
configure(HttpSecurity)
用于配置保护授权端点的HttpSecurity过滤器链
@Override
protected void configure(HttpSecurity http) throws Exception {
// 创建1个 【AuthorizationServerSecurityConfigurer授权服务器安全配置器】, 用来继续配置HttpSecurity
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
// 从AuthorizationServerEndpointsConfiguration配置类中的endpoints属性中的的frameworkEndpointHandlerMapping属性
FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping();
// 将FrameworkEndpointHandlerMapping设置到HttpSecurity的sharedObjects属性中
http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping);
// 遍历所有的 *授权服务器配置器* 来配置 【AuthorizationServerSecurityConfigurer授权服务器安全配置器】-扩展点
configure(configurer);
// 将【AuthorizationServerSecurityConfigurer授权服务器安全配置器】添加到HttpSecurity中
http.apply(configurer);
// 令牌端点路径、jwt令牌公钥端点路径、校验令牌端点路径,
// 会添加上FrameworkEndpointHandlerMapping所设置的前缀(如果有设置的话)
String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token");
String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key");
String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token");
// 1. 如果AuthorizationServerEndpointsConfiguration的endpoints属性的userDetailsServiceOverride属性为false,
// 即未给AuthorizationServerEndpointsConfigurer配置userDetailsService
// 此时会获取HttpSecurity的sharedObjects属性中的UserDetailsService类型的公共属性
// 2. 那么是在哪里将UserDetailsService类型的公共属性设置到HttpSecurity的sharedObjects属性中的呢?
// 是在: WebSecurityConfigurerAdapter的createSharedObjects()方法中
if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {
UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class);
endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);
}
http
// 用来构建FilterSecurityInterceptor这个重要的过滤器的权限校验规则, 直接跳到AbstractInterceptUrlConfigurer这个类就可以看到
.authorizeRequests()
// 令牌端点 /oauth/token, 必须完全认证才可以访问
.antMatchers(tokenEndpointPath).fullyAuthenticated()
// jwt令牌公钥端点路径 /oauth/token_key , 可以配置访问规则, 可使用spel表达式
.antMatchers(tokenKeyPath).access(configurer.getTokenKeyAccess())
// 校验令牌端点路径 /oauth/check_token , 可以配置访问规则, 可使用spel表达式
.antMatchers(checkTokenPath).access(configurer.getCheckTokenAccess())
.and()
// 设置匹配到当前过滤器链的条件,
// 这里的意思是: 当前构建的这条HttpSecurity过滤器链只拦截这3条路径的请求, 其它的请求不会匹配到当前的这条过滤器链
// 这说明: @EnableAuthorizationServer注解会引入1条过滤器链, 来保护授权端点
.requestMatchers().antMatchers(tokenEndpointPath, tokenKeyPath, checkTokenPath)
.and()
// 添加会话管理配置器
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
// 将clientDetailsService作为ClientDetailsService类型, 设置到HttpSecurity的sharedObjects属性中
http.setSharedObject(ClientDetailsService.class, clientDetailsService);
}
AuthorizationServerSecurityConfigurer
AuthorizationServerSecurityConfiguration作为WebSecurityConfigurerAdapter的子类,为HttpSecurity添加了1个配置类:AuthorizationServerSecurityConfigurer授权服务器安全配置器。
可以定义AuthorizationServerConfigurer 授权服务器配置器来配置该 AuthorizationServerSecurityConfigurer授权服务器安全配置器
// 认证入口, 设置未认证时该如何
AuthenticationEntryPoint authenticationEntryPoint;
// 访问拒绝处理器, 设置没有权限时该如何
AccessDeniedHandler accessDeniedHandler = new OAuth2AccessDeniedHandler();
// 给客户端认证校验密码
PasswordEncoder passwordEncoder;
String realm = "oauth2/client";
// 是否支持客户端表单认证方式, 如果支持, 将会添加 ClientCredentialsTokenEndpointFilter 到过滤器链中
// 因为令牌端点是需要客户端来发送获取令牌请求的, 所以这里就提供了该过滤器来支持客户端通过表单提交客户端id和客户端密码来认证客户端
//(这个过滤器一般放在BasicAuthenticationFilter的前面, 一般来说, 不推荐开启, 而推荐使用Basic认证, 即使用BasicAuthenticationFilter来对客户端认证, 所谓的basic认证就是, 客户端发送请求过来时, 添加Authorization请求头, 值为: Basic + 空格 + Base64(clientId:clientSecret) )
boolean allowFormAuthenticationForClients = false;
// jwt令牌密钥访问规则, 默认拒绝所有访问
String tokenKeyAccess = "denyAll()";
// 校验令牌访问规则, 默认拒绝所有访问
String checkTokenAccess = "denyAll()";
// 开启ssl
boolean sslOnly = false;
// 允许添加自定义过滤器, 这些添加的过滤器将添加到BasicAuthenticationFilter的前面
List<Filter> tokenEndpointAuthenticationFilters = new ArrayList();
init(HttpSecurity)
用来添加BasicAuthenticationFilter过滤器的
@Override
public void init(HttpSecurity http) throws Exception {
// 1. 其实就是给HttpSecurity过滤器链上的异常过滤器设置入口点属性,
// 2. 如果当前AuthorizationServerSecurityConfigurer授权服务器安全配置器未设置authenticationEntryPoint属性,
// 则会创建1个BasicAuthenticationEntryPoint添加到ExceptionHandlingConfigurer的defaultEntryPointMappings中, 并以MediaTypeRequestMatcher实例作为key
registerDefaultAuthenticationEntryPoint(http);
// 如果密码匹配器不为空(它用于客户端密码的校验)
if (passwordEncoder != null) {
// 1. 先拿到HttpSecurity的sharedObjects属性中的ClientDetailsService的公共属性,
// 这个ClientDetailsService的公共属性在 AuthorizationServerSecurityConfiguration 的 configure(HttpSecurity)方法中设置
// 2. 使用 ClientDetailsUserDetailsService 将 ClientDetailsService 包装, 来实现UserDetailsService接口,
// 这样当认证的时候, 就会把username作为clientId传给ClientDetailsService得到ClientDetails,
// 然后将ClientDetails的客户端id, 客户端密码, 客户端权限 封装为1个User作为UserDetails的实现返回
ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(clientDetailsService());
// 如果clientDetails未设置密码, 那么使用此passwordEncoder对空字符生成1个密码作为客户端密码设置到clientDetails中
clientDetailsUserDetailsService.setPasswordEncoder(passwordEncoder());
// 1. 先拿到HttpSecurity的sharedObjects属性中的AuthenticationManagerBuilder的公共属性,
// 这个AuthenticationManagerBuilder的公共属性, 在 WebSecurityConfigurerAdapter 的 getHttp()方法中, 在创建HttpSecurity时,
// 就会把WebSecurityConfigurerAdapter的authenticationBuilder属性作为AuthenticationManagerBuilder传进去作为HttpSecurity的公共属性
// 2. 将刚刚创建的clientDetailsUserDetailsService作为此AuthenticationManagerBuilder的defaultUserDetailsService属性,
// 并且给此AuthenticationManagerBuilder添加1个DaoAuthenticationConfigurer, 并且将clientDetailsUserDetailsService作为userDetailsService属性,
// 此clientDetailsUserDetailsService将会设置到DaoAuthenticationConfigurer的DaoAuthenticationProvider中用于查询用户
// 3. 何处被引用? 在HttpBasicConfigurer的configure(HttpSecurity)方法中,
// 会从HttpSecurity的sharedoBjects属性中获取AuthenticationManagerBuilder类型的公共属性, 添加到BasicAuthenticationFilter过滤器中
// 该BasicAuthenticationFilter过滤器会添加到HttpSecurity过滤器链中
// 4. 将passwordEncoder设置给DaoAuthenticationConfigurer 从而设置给 DaoAuthenticationProvider, 用于客户端密码校验
http.getSharedObject(AuthenticationManagerBuilder.class)
.userDetailsService(clientDetailsUserDetailsService)
.passwordEncoder(passwordEncoder());
}
else {
// 1. 这里其实就是: http.getSharedObject(AuthenticationManagerBuilder.class).userDetailsService(new ClientDetailsUserDetailsService(clientDetailsService())) 与上面几乎一致,
// 2. 不同在于上面给的设置了指定的passwordEncoder, 而这里未指定, 如果未指定的话, 在DaoAuthenticationProvider的唯一无参构造方法中,
// 就会使用PasswordEncoderFactories.createDelegatingPasswordEncoder()创建1个密码匹配器;
// 这里未将此ClientDetailsUserDetailsService设置到AuththenticationManagerBuilder的defaultUserDetailsService属性中哦;
http.userDetailsService(new ClientDetailsUserDetailsService(clientDetailsService()));
}
http
// 添加SecurityContext配置器
.securityContext().securityContextRepository(new NullSecurityContextRepository())
.and()
// 移除csrf配置器
.csrf().disable()
// 添加HttpBasic配置器, 用于添加BasicAuthenticationFilter过滤器
.httpBasic().realmName(realm);
// 如果只支持ssl的话, 就会添加ChannelSecurityConfigurer配置器, 而添加ChannelProcessingFilter
if (sslOnly) {
http.requiresChannel().anyRequest().requiresSecure();
}
}
configure(HttpSecurity)
其实就是用来添加ClientCredentialsTokenEndpointFilter过滤器的
@Override
public void configure(HttpSecurity http) throws Exception {
// 1. 就是拿一下HttpSecurity的sharedObjects属性中的FrameworkEndpointHandlerMapping的公共属性, 啥也没干
// 2. 在AuthorizationServerSecurityConfiguration的configure(HttpSecurity)方法中就向HttpSecurity的sharedObjects属性中设置了FrameworkEndpointHandlerMapping类型的的公共属性
frameworkEndpointHandlerMapping();
// 如果允许客户端使用表单认证的方式(不推荐使用该方式, 推荐使用Basic认证方式)
if (allowFormAuthenticationForClients) {
// 就是往HttpSecurity过滤器链中添加ClientCredentialsTokenEndpointFilter过滤器
// 该ClientCredentialsTokenEndpointFilter过滤器会从HttpSecurity的sharedObjects属性中获取到AuthenticationManager类型的公共属性, 该属性在HttpSecurity的beforeConfigure()方法中设置进去的
clientCredentialsTokenEndpointFilter(http);
}
// 允许在BasicAuthenticationFilter过滤器的前端添加自定义的过滤器
for (Filter filter : tokenEndpointAuthenticationFilters) {
http.addFilterBefore(filter, BasicAuthenticationFilter.class);
}
// 给异常处理过滤器设置访问拒绝处理器
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}