Security OAuth2 SSO单点登录源码剖析ing...(二)
文章目录
- 单点登录简介
- SSO客户端
- @EnableOAuth2Sso注解
- @EnableOAuth2Client注解
- OAuth2SsoProperties
- OAuth2SsoDefaultConfiguration
- OAuth2SsoCustomConfiguration
- ResourceServerTokenServicesConfiguration
- 过滤器
- 过滤器的顺序早已约定FilterComparator
- 过滤器配置器顺序说明
- OAuth2ClientContextFilter
- OAuth2ClientAuthenticationProcessingFilter
- SSO服务端
Security OAuth2 SSO单点登录源码剖析 - 自己总结
单点登录简介
SSO客户端
@EnableOAuth2Sso注解
@EnableOAuth2Sso是sso客户端的核心注解,它为sso客户端引入了与单点登录相关的配置。
由该注解引入的配置包括:
- @EnableOAuth2Client、
- OAuth2SsoProperties、
- OAuth2SsoDefaultConfiguration、
- OAuth2SsoCustomConfiguration、
- ResourceServerTokenServicesConfiguration
@EnableOAuth2Client注解
使用@Import注解引入了OAuth2ClientConfiguration配置,使用@Bean方式定义了OAuth2ClientContextFilter过滤器
,嵌入到Web容器中(注意,是Web容器,并不是security的过滤器链中),当后续的过滤器抛出UserRedirectRequiredException这种类型的异常时,就会让用户重定向到认证中心去做登录。那么,就要保证OAuth2ClientContextFilter过滤器一定是要排在FilterChainProxy的前面的,从【OAuth2授权流程和源码解析】的【FilterChainProxy与其它的web的Filter的顺序】章节,可以知道默认的springSecurityFilterChain的排序值为-100,如果要排在它的前面,那么就需要小于-100,我们可以看到在OAuth2AutoConfiguration自动配置类中引入了OAuth2RestOperationsConfiguration配置
类,这个配置类中就注入了上面使用@Bean方式定义的OAuth2ClientContextFilter过滤器,并且使用FilterRegistrationBean将它包装并交给spring容器,注意到它设置的order恰好就是:-100-10 = -100,比-100要小
,因此,OAuth2ClientContextFilter排在springSecurityFilterChain的前面是没有问题的。
OAuth2SsoProperties
配置前缀为:security.oauth2.sso,可配置就只是:security.oauth2.sso.login-path = /login
当用户未登录时,访问sso客户端资源,如:http://localhost:1111
,抛出认证异常或访问拒绝异常而被异常处理器捕捉到该异常,就会将用户重定向到sso客户端的该路径下,如:http://localhost:1111/login
,但此时会让OAuth2ClientAuthenticationProcessingFilter认证过滤器满足此路径匹配要求,因为现在没有任何令牌相关的信息,所以就会抛出UserRedirectRequiredException。而这种类型的异常一旦抛出,就会让OAuth2ClientContextFilter这个过滤器捕捉到,这个OAuth2ClientContextFilter过滤器就会让用户去重定向到认证服务器授权页面去授权:http://localhost:1110/oauth/authorize?client_id=c1&redirect_uri=http://localhost:1111/login&response_type=code&state=Z2Io35
但由于当前用户在认证中心未登录,就会又被认证服务器重定向到认证登录页面,当用户登录完成时,按照OAuth2授权码一般的授权逻辑,就会重定向到授权页面,但这里会由于走自动授权模式,因此就直接重定向到了:http://localhost:1111/login?code=cSdvsL&state=M28wuM
,此时就携带了code和state,而一旦拿到了code和state,在OAuth2ClientAuthenticationProcessingFilter过滤器中,就可以按照授权码模式的逻辑获取访问令牌,获取用户信息了
OAuth2SsoDefaultConfiguration
OAuth2SsoCustomConfiguration
ResourceServerTokenServicesConfiguration
过滤器
过滤器的顺序早已约定FilterComparator
这在HttpSecurity的performBuild()方法中,就会对filters属性中所添加的所有过滤器进行排序,排序的规则就是使用的是FilterComparator
final class FilterComparator implements Comparator<Filter>, Serializable {private static final int INITIAL_ORDER = 100;private static final int ORDER_STEP = 100;private final Map<String, Integer> filterToOrder = new HashMap<>();FilterComparator() {Step order = new Step(INITIAL_ORDER, ORDER_STEP);put(ChannelProcessingFilter.class, order.next());put(ConcurrentSessionFilter.class, order.next());put(WebAsyncManagerIntegrationFilter.class, order.next());put(SecurityContextPersistenceFilter.class, order.next());put(HeaderWriterFilter.class, order.next());put(CorsFilter.class, order.next());put(CsrfFilter.class, order.next());put(LogoutFilter.class, order.next());filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter", order.next());filterToOrder.put("org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter", order.next());put(X509AuthenticationFilter.class, order.next());put(AbstractPreAuthenticatedProcessingFilter.class, order.next());filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter", order.next());filterToOrder.put("org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter", order.next());put(UsernamePasswordAuthenticationFilter.class, order.next());put(ConcurrentSessionFilter.class, order.next());filterToOrder.put("org.springframework.security.openid.OpenIDAuthenticationFilter", order.next());put(DefaultLoginPageGeneratingFilter.class, order.next());put(DefaultLogoutPageGeneratingFilter.class, order.next());put(ConcurrentSessionFilter.class, order.next());put(DigestAuthenticationFilter.class, order.next());filterToOrder.put("org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter", order.next());put(BasicAuthenticationFilter.class, order.next());put(RequestCacheAwareFilter.class, order.next());put(SecurityContextHolderAwareRequestFilter.class, order.next());put(JaasApiIntegrationFilter.class, order.next());put(RememberMeAuthenticationFilter.class, order.next());put(AnonymousAuthenticationFilter.class, order.next());filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter", order.next());put(SessionManagementFilter.class, order.next());put(ExceptionTranslationFilter.class, order.next());put(FilterSecurityInterceptor.class, order.next());put(SwitchUserFilter.class, order.next());}// 排序方法实现public int compare(Filter lhs, Filter rhs) {Integer left = getOrder(lhs.getClass());Integer right = getOrder(rhs.getClass());return left - right;}// ...
}