资源服务器核心源码分析
OAuth2AuthenticationProcessingFilter
资源服务器的核心是OAuth2AuthenticationProcessingFilter过滤器。它被插到配置为资源端口的过滤器链中,主要功能是获取请求中携带的access_token中,通过access_token提取OAuth2Authentication并存入Spring Security上下文。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
final boolean debug = logger.isDebugEnabled();
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
try {
//提取请求携带的token,构建一个认证的Authentication对象
Authentication authentication = tokenExtractor.extract(request);
if (authentication == null) {
if (stateless && isAuthenticated()) {
if (debug) {
logger.debug("Clearing security context.");
}
SecurityContextHolder.clearContext();
}
if (debug) {
logger.debug("No token in request, will continue chain.");
}
}
else {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
if (authentication instanceof AbstractAuthenticationToken) {
AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
}
//获取token携带的认证信息,OAuth2AuthenticationManager主要做了3件事
//1.通过token获取用户的OAuth2Authentication对象
//2.验证访问资源resourceId是否符合范围
//3.验证客户端访问的scope
Authentication authResult = authenticationManager.authenticate(authentication);
if (debug) {
logger.debug("Authentication success: " + authResult);
}
eventPublisher.publishAuthenticationSuccess(authResult);
//将当前的Authentication放入到Context中,访问后面的资源
SecurityContextHolder.getContext().setAuthentication(authResult);
}
}
catch (OAuth2Exception failed) {
SecurityContextHolder.clearContext();
if (debug) {
logger.debug("Authentication request failed: " + failed);
}
eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed),
new PreAuthenticatedAuthenticationToken("access-token", "N/A"));
authenticationEntryPoint.commence(request, response,
new InsufficientAuthenticationException(failed.getMessage(), failed));
return;
}
chain.doFilter(request, response);
}
- TokenExtractor的默认实现类BearerTokenExtractor
- AuthenticationManager的默认实现类是OAuth2AuthenticationManager
TokenExtractor
接口的功能是提取请求中包含的access_token,目前只有一个实现类:BearerTokenExtractor,它只用于提取Bearer类型的access_token。请求中携带的access_token参数即可以放在HTTP请求头中,也可以在HTTP请求参数中。核心源码如下:
@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) {
// 首先从header中解析access_token
String token = extractHeaderToken(request);
// 然后从request parameter中access_token
if (token == null) {
logger.debug("Token not found in headers. Trying request parameters.");
token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
if (token == null) {
logger.debug("Token not found in request parameters. Not an OAuth2 request.");
}
else {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
}
}
return token;
}
OAuth2AuthenticationManager
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication == null) {
throw new InvalidTokenException("Invalid token (token not found)");
} else {
String token = (String)authentication.getPrincipal();
//借助tokenServices,根据token加载身份信息
OAuth2Authentication auth = this.tokenServices.loadAuthentication(token);
if (auth == null) {
throw new InvalidTokenException("Invalid token: " + token);
} else {
Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
if (this.resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(this.resourceId)) {
throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + this.resourceId + ")");
} else {
this.checkClientDetails(auth);
if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails();
if (!details.equals(auth.getDetails())) {
details.setDecodedDetails(auth.getDetails());
}
}
auth.setDetails(authentication.getDetails());
auth.setAuthenticated(true);
return auth;
}
}
}
}
其中,最关键的tokenServices是ResourceServerTokenServices的实例。ResourceServerTokenServices接口的最主要的2个实现类是RemoteTokenServices和DefaultTokenServices。下面的断点中,我用的是远程调用,RemoteTokenServices
下面是我部分断点的图,比较简单,就不说明了,其中pig的官网也有说明,连接:pig 校验令牌详解 【原理】 · 语雀 (yuque.com)