Oauth2的资源服务器核心源码分析

资源服务器核心源码分析

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)

Oauth2的资源服务器核心源码分析

Oauth2的资源服务器核心源码分析

Oauth2的资源服务器核心源码分析

Oauth2的资源服务器核心源码分析

上一篇:OAuth2


下一篇:Spring Security+OAuth2 精讲 多场景打造企业级认证与授权