OAuth2AuthenticationManager源码跟踪

本文仅助那些在Security集成OAuth2.0路上踩坑的人理解认证过程。

OAuth2AuthenticationManager

首先,我觉得分析OAuth2AuthenticationManager,我们需要先理解它是如何被使用,何时被使用,那我们就从这里开始逐一分析吧,我手画了一张图作为索引,以便我们理解,其中蓝色的为类,绿色的为接口,箭头指向的方向是实现类或者父类。

因为里面接口和方法过多,所以不做展示,下面具体来进行代码分析。

 public class OAuth2AuthenticationManager implements AuthenticationManager, InitializingBean {

	private ResourceServerTokenServices tokenServices;

	private ClientDetailsService clientDetailsService;

	private String resourceId;

	public void setResourceId(String resourceId) {
		this.resourceId = resourceId;
	}

	public void setClientDetailsService(ClientDetailsService clientDetailsService) {
		this.clientDetailsService = clientDetailsService;
	}

	public void setTokenServices(ResourceServerTokenServices tokenServices) {
		this.tokenServices = tokenServices;
	}

	public void afterPropertiesSet() {
		Assert.state(tokenServices != null, "TokenServices are required");
	}

(3)	public Authentication authenticate(Authentication authentication) throws AuthenticationException {

		if (authentication == null) {
			throw new InvalidTokenException("Invalid token (token not found)");
		}
(3).1		String token = (String) authentication.getPrincipal();
(3).2		OAuth2Authentication auth = tokenServices.loadAuthentication(token);
		if (auth == null) {
			throw new InvalidTokenException("Invalid token: " + token);
		}

		Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
		if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
			throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")");
		}

(3).3		checkClientDetails(auth);

		if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
			OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
			// Guard against a cached copy of the same details
			if (!details.equals(auth.getDetails())) {
				// Preserve the authentication details from the one loaded by token services
				details.setDecodedDetails(auth.getDetails());
			}
		}
		auth.setDetails(authentication.getDetails());
(3).4		auth.setAuthenticated(true);
		return auth;

	}

	private void checkClientDetails(OAuth2Authentication auth) {
		if (clientDetailsService != null) {
			ClientDetails client;
			try {
				client = clientDetailsService.loadClientByClientId(auth.getOAuth2Request().getClientId());
			}
			catch (ClientRegistrationException e) {
				throw new OAuth2AccessDeniedException("Invalid token contains invalid client id");
			}
			Set<String> allowed = client.getScope();
			for (String scope : auth.getOAuth2Request().getScope()) {
				if (!allowed.contains(scope)) {
					throw new OAuth2AccessDeniedException(
							"Invalid token contains disallowed scope (" + scope + ") for this client");
				}
			}
		}
	}

}

OAuth2AuthenticationManager的成员变量

ResourceServerTokenServices

以上是它的关系图,关于该接口,了解即可,不必过于深究,有兴趣的小伙伴们可以去翻看源码,学习一下代码结构也是不错的。
DefaultTokenServices 利用Security内置的令牌存储器(Tokenstore)接口进行数据库的CRUD操作,查看数据库结构及表说明

ClientDetailsService


ClientDetailsService 用于加载客户端,有两种实现方式,一种是基于内存,一种是基于存储库的方式。

auth-server: http://localhost:18081/uac
server:
  port: 18082

security:
  oauth2:
    client:
      client-id: client1
      client-secret: 201314
      user-authorization-uri: ${auth-server}/oauth/authorize
      access-token-uri: ${auth-server}/oauth/token
    resource:
      jwt:
        key-uri: ${auth-server}/oauth/token_key
        key-value: 201314

上面是关于客户端的配置,auth-server 为资源服务器路径。

authenticate()

  • (3).1处的代码,期望传入的身份验证请求具有一个主体值,该主体值是一个访问令牌值(一般在(authorization header)请求头中)
  • (3).2处的代码,ResourceServerTokenServices通过查询数据库中 oauth_client_details该表,加载身份验证。
  • (3).3处的代码,检查资源id是否包含在授权请求中。
  • (3).4,通过身份认证,Security将OAuth2Authentication对象存入Session中,然后跳转到AuthorizationEndpointauthorize()。该方法跳出一个授权页面,提供授权的通过权或否决权。

以上就是我对于OAuth2AuthenticationManager源码的理解,仅供参考,如有不正确的地方,请提出指正。

posted @ 2020-03-14 19:16  喜欢你的眉眼微笑i  阅读(2034)  评论(0编辑  收藏  举报