Spring Security-01 /oauth/token执行授权流程 分析

内容简介:
个人服务器推荐:

PS: 若文章字体偏大或者偏小,建议通过 ctrl键+鼠标滑轮 进行修改,以提升阅读效果.(带来不便,请谅解!)


Version:

  • JDK : 1.8
    1. 首先 Spring Security 校验用户request 请求. ->spring security filter chain 过滤操作
    2. TokenEndpoint : /oauth/token 执行授权流程.

简述:

  1. client 信息 认证, -> http basic + spring security filter

    授权前端 通过在reqeust header 中添加http basic 中添加client信息 用于 客户端身份认证.

    前端: basic64 加密, 内容是test:test

    ​ header添加Authorization:Basic dGVzdDp0ZXN0

    后端: spring security filter 过滤链处理

  2. oauth2 认证 即 TokenEndpoint的/oauth/token 方法实现用户登录.

Content:

TokenEndpoint:

/oauth/token:

先认证用户,在授权用户信息.

*相当于执行controller层方法.**

前端http请求:

```

```txt
************************************************************

Request received for POST '/oauth/token?randomStr=55271651050219816&code=3&grant_type=password&scope=server':

HttpServletRequestImpl [ POST /oauth/token ]
servletPath:/oauth/token
pathInfo:null
headers: 
Origin: http://tx-year
Cookie: JSESSIONID=YomwMhfw67uDUjybVaFpRM4G99obWqJdww2orntS
X-Forwarded-Prefix: /auth
Accept: application/json, text/plain, */*
Referer: http://tx-year/
X-Forwarded-Host: 172.24.93.133:9999
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
X-Forwarded-Proto: http,http
Accept-Encoding: gzip, deflate
DNT: 1
Pragma: no-cache
X-Forwarded-Port: 9999
isToken: false
Authorization: Basic cGlnOnBpZw==
Cache-Control: no-cache
host: 192.168.80.1:3000
Forwarded: proto=http;host="172.24.93.133:9999";for="172.24.100.1:43090"
X-Forwarded-For: 112.234.54.246,172.24.100.1
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Content-Length: 30
X-Real-IP: 112.234.54.246
Content-Type: application/x-www-form-urlencoded

pig 
Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  LogoutFilter
  ClientCredentialsTokenEndpointFilter
  BasicAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]
fun 
Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  LogoutFilter
  ClientCredentialsTokenEndpointFilter
  BasicAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]
************************************************************


  • TokenEndpoint

    做了三件事:

    1. 根据clientId 获取 Oauth client, -> ClientDetailsService

    2. 构建OauthTOkenRequest

    3. 生成Oauth2AccessToken ->

      OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);

      按照request中的授权类型 grant Oauth2AccessToken

      如果是password 模式, ->ResourceOwnerPasswordTokenGranter.grant()

      最后会调到 UserDetailsService.loadUserByUsername(username)

@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
	public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
	Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
		// 1. 校验用户是否 已认证
		if (!(principal instanceof Authentication)) {
			throw new InsufficientAuthenticationException(
					"There is no client authentication. Try adding an appropriate authentication filter.");
		}
		// 2. 获取oauth client , 根据clientID
		String clientId = getClientId(principal);
		ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
		// 3. 构建tokenRequest
		TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
		// 4. client 是否已认证
		if (clientId != null && !clientId.equals("")) {
			// Only validate the client details if a client authenticated during this
			// request.
			if (!clientId.equals(tokenRequest.getClientId())) {
				// double check to make sure that the client ID in the token request is the same as that in the
				// authenticated client
				throw new InvalidClientException("Given client ID does not match authenticated client");
			}
		} // 5. scope 校验
		if (authenticatedClient != null) {
			oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
		}
		if (!StringUtils.hasText(tokenRequest.getGrantType())) {
			throw new InvalidRequestException("Missing grant type");
		}
		if (tokenRequest.getGrantType().equals("implicit")) {
			throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
		}
		// 5.1grant_type=authorization_code ,清空scope
		if (isAuthCodeRequest(parameters)) {
			// The scope was requested or determined during the authorization step
			if (!tokenRequest.getScope().isEmpty()) {
				logger.debug("Clearing scope of incoming token request");
				tokenRequest.setScope(Collections.<String> emptySet());
			}
		}
		// 5.2grant_type=refresh_token ,设置scope
		if (isRefreshTokenRequest(parameters)) {
			// A refresh token has its own default scopes, so we should ignore any added by the factory here.
			tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
		}
		// 6. 验证grant_type,生成 token
		OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
		if (token == null) {
			throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
		}

		return getResponse(token);

	}

总结:

  1. client 信息 认证, -> http basic + spring security filter

    授权前端 通过在reqeust header 中添加http basic 中添加client信息 用于 客户端身份认证.

    前端: basic64 加密, 内容是test:test

    ​ header添加Authorization:Basic dGVzdDp0ZXN0

    后端: spring security filter 过滤链处理

  2. oauth2 认证 即 TokenEndpoint的/oauth/token 方法实现用户登录.

参考: