SpringSecurityOauth2授权源码解析(二)

阅读: 评论:0

SpringSecurityOauth2授权源码解析(⼆)
上⼀篇⽂章中我们介绍了获取token的流程,这⼀篇重点分析⼀下,携带token访问受限资源时,内部的⼯作流程。
@EnableResourceServer与@EnableAuthorizationServer
还记得我们在第⼀节中就介绍过了OAuth2的两个核⼼概念,资源服务器与⾝份认证服务器。我们对两个注解进⾏配置的同时,到底触发了内部的什么相关配置呢?
上⼀篇⽂章重点介绍的其实是与⾝份认证相关的流程,即如何获取token,⽽本节要分析的携带token访问受限资源,⾃然便是与
@EnableResourceServer相关的资源服务器配置了。
我们注意到其相关配置类是ResourceServerConfigurer,内部关联了ResourceServerSecurityConfigurer和HttpSecurity。前者与资源安全配置相关,后者与http安全配置相关。(类名⽐较类似,注意区分,以Adapter结尾的是适配器,以Configurer结尾的是配置器,以Builder结尾的是建造器,他们分别代表不同的设计模式,对设计模式有所了解可以更加⽅便理解其设计思路)
public class ResourceServerConfigurerAdapter implements ResourceServerConfigurer {
@Override
public void configure(ResourceServerSecurityConfigurer resources <1> ) throws Exception {
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
<1> ResourceServerSecurityConfigurer显然便是我们分析的重点了。ResourceServerSecurityConfigurer(了解)
其核⼼配置如下所⽰:
public void configure(HttpSecurity http) throws Exception {
AuthenticationManager oauthAuthenticationManager = oauthAuthenticationManager(http);
resourcesServerFilter = new OAuth2AuthenticationProcessingFilter();//<1>
resourcesServerFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager);//<2>
if (eventPublisher != null) {
resourcesServerFilter.setAuthenticationEventPublisher(eventPublisher);
}
if (tokenExtractor != null) {
resourcesServerFilter.setTokenExtractor(tokenExtractor);//<3>
}
resourcesServerFilter = postProcess(resourcesServerFilter);
resourcesServerFilter.setStateless(stateless);
// @formatter:off
http
.authorizeRequests().expressionHandler(expressionHandler)
.and()
.addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class)
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)//<4>
.authenticationEntryPoint(authenticationEntryPoint);
// @formatter:on
}
这段是整个oauth2与HttpSecurity相关的核⼼配置,其中有⾮常多的注意点,顺带的都强调⼀下:
<1> 创建OAuth2AuthenticationProcessingFilter,即下⼀节所要介绍的OAuth2核⼼过滤器。
<2> 为OAuth2AuthenticationProcessingFilter提供固定的AuthenticationManager即OAuth2AuthenticationManager,它并没有将OAuth2AuthenticationManager添加到spring的容器中,不然可能会影响spring security的普通认证流程(⾮oauth2请求),只有被OAuth2AuthenticationProcessingFilter拦截到的oauth2相关请求才被特殊的⾝份认证器处理。
<3> 设置了TokenExtractor默认的实现—-BearerTokenExtractor,这个类在下⼀节介绍。
<4> 相关的异常处理器,可以重写相关实现,达到⾃定义异常的⽬的。
还记得我们在⼀开始的配置中配置了资源服务器,是它触发了相关的配置。
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {}
核⼼过滤器 OAuth2AuthenticationProcessingFilter(掌握)
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain){
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
try {
//从请求中取出⾝份信息,即access_token
声源定位Authentication authentication = act(request);
if (authentication == null) {
...
}
else {
ccenterrequest.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, Principal());
if (authentication instanceof AbstractAuthenticationToken) {
AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
}
//认证⾝份
Authentication authResult = authenticationManager.authenticate(authentication);
...
eventPublisher.publishAuthenticationSuccess(authResult);
//将⾝份信息绑定到SecurityContextHolder中
}
}
不锈钢勾花网catch (OAuth2Exception failed) {
...
return;
}
chain.doFilter(request, response);
}
整个过滤器便是oauth2⾝份鉴定的关键,在源码中,对这个类有⼀段如下的描述
A pre-authentication filter for OAuth2 protected resources. Extracts an OAuth2 token from the incoming request and
uses it to populate the Spring Security context with an {@link OAuth2Authentication}
(if used in conjunction with an {@link OAuth2AuthenticationManager}).
水气分离器OAuth2保护资源的预先认证过滤器。如果与OAuth2AuthenticationManager结合使⽤,
则会从到来的请求之中提取⼀个OAuth2 token,之后使⽤OAuth2Authentication来
填充Spring Security上下⽂。
其中涉及到了两个关键的类TokenExtractor,AuthenticationManager。相信后者这个接⼝⼤家已经不陌⽣,但前⾯这个类之前还未出现在我们的视野中。
OAuth2的⾝份管理器–OAuth2AuthenticationManager(掌握)
在之前的OAuth2核⼼过滤器中出现的AuthenticationManager其实在我们意料之中,携带access_token必定得经过⾝份认证,但是在我们debug进⼊其中后,发现了⼀个出乎意料的事,AuthenticationManager的实现类并不是我们在前⾯⽂章中聊到的常⽤实现类ProviderManager,⽽是OAuth2AuthenticationManager。
回顾我们第⼀篇⽂章的配置,压根没有出现过这个OAuth2AuthenticationManager,并且它脱离了我们熟悉的认证流程(第⼆篇⽂章中的认证管理器UML图是⼀张经典的spring security结构类图),它直接重写了容器的顶级⾝份认证接⼝,内部维护了⼀个ClientDetailService和ResourceServerTokenServices,这两个核⼼类在 有分析过。在ResourceServerSecurityConfigurer的⼩节中我们已经知晓了它是如何被框架⾃动配置的,这⾥要强调的是OAuth2AuthenticationManager是密切与token认证相关的,⽽不是与获取token密切相关的。
其判别⾝份的关键代码如下:
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
...
地铁门
String token = (String) Principal();
//最终还是借助tokenServices根据token加载⾝份信息
OAuth2Authentication auth = tokenServices.loadAuthentication(token);
...
checkClientDetails(auth);
if (Details() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) Details();
...
}
auth.Details());
auth.setAuthenticated(true);
return auth;
}
说到tokenServices这个密切与token相关的接⼝,这⾥要强调下,避免产⽣误解。tokenServices分为两类,⼀个是⽤在AuthenticationServer端,第⼆篇⽂章中介绍的
public interface AuthorizationServerTokenServices {
//创建token
OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException;
//刷新token
OAuth2AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest)
throws AuthenticationException;
//获取token
OAuth2AccessToken getAccessToken(OAuth2Authentication authentication);
}
⽽在ResourceServer端有⾃⼰的tokenServices接⼝:
public interface ResourceServerTokenServices {
//根据accessToken加载客户端信息
OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException;
//根据accessToken获取完整的访问令牌详细信息。
OAuth2AccessToken readAccessToken(String accessToken);
}
具体内部如何加载,和AuthorizationServer⼤同⼩异,只是从tokenStore中取出相应⾝份的流程有点区别,不再详细看实现类了。TokenExtractor(了解)
这个接⼝只有⼀个实现类,⽽且代码⾮常简单
public class BearerTokenExtractor implements TokenExtractor {
private final static Log logger = Log(BearerTokenExtractor.class);
@Override
public Authentication extract(HttpServletRequest request) {
String tokenValue = extractToken(request);
if (tokenValue != null) {
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(tokenValue, "");
return authentication;
}
return null;
}
protected String extractToken(HttpServletRequest request) {
// first check
String token = extractHeaderToken(request);
// bearer type allows a request parameter as well
if (token == null) {
...
//从requestParameter中获取token
}
return token;
}
/
**
* Extract the OAuth bearer token from a header.
*/
protected String extractHeaderToken(HttpServletRequest request) {
Enumeration<String> headers = Headers("Authorization");
while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that)
...
//从Header中获取token
}
return null;
}
电子档案袋
}
异常处理
OAuth2在资源服务器端的异常处理不算特别完善,但基本够⽤,如果想要重写异常机制,可以直接替换掉相关的Handler,如权限相关的AccessDeniedHandler。具体的配置应该在@EnableResourceServer中被覆盖,这是适配器+配置器的好处。
总结
到这⼉,Spring Security OAuth2的整个内部流程就算是分析结束了。本系列的⽂章只能算是揭⽰⼀个⼤概的流程,重点还是介绍相关设计+接⼝,想要了解更多的细节,需要⾃⼰去翻看源码,研究各个实现类。在分析源码过程中总结出的⼀点经验,与君共勉:

本文发布于:2023-07-20 23:41:31,感谢您对本站的认可!

本文链接:https://patent.en369.cn/patent/2/185096.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:配置   相关   资源   流程   认证   分析
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 369专利查询检索平台 豫ICP备2021025688号-20 网站地图