擴充套件資源伺服器解決oauth2 效能瓶頸

冷冷gg發表於2019-03-20

擴充套件資源伺服器解決oauth2 效能瓶頸

  • 使用者攜帶token 請求資源伺服器
  • 資源伺服器攔截器 攜帶token 去認證伺服器 呼叫tokenstore 對token 合法性校驗
  • 資源伺服器拿到token,預設只會含有使用者名稱資訊
  • 通過使用者名稱呼叫userdetailsservice.loadbyusername 查詢使用者全部資訊

詳細效能瓶頸分析,請參考上篇文章《擴充套件jwt解決oauth2 效能瓶頸》
本文是針對傳統使用UUID token 的情況進行擴充套件,提高系統的吞吐率,解決效能瓶頸的問題

預設check-token 解析邏輯

  • RemoteTokenServices 入口
@Override
public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {

	MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
	formData.add(tokenName, accessToken);
	HttpHeaders headers = new HttpHeaders();
	headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret));
	// 呼叫認證伺服器的check-token 介面檢查token
	Map<String, Object> map = postForMap(checkTokenEndpointUrl, formData, headers);

    return tokenConverter.extractAuthentication(map);
}
複製程式碼
  • 解析認證伺服器返回的資訊
    DefaultAccessTokenConverter
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
		Map<String, String> parameters = new HashMap<String, String>();
		Set<String> scope = extractScope(map);
		// 主要是 使用者的資訊的抽取
		Authentication user = userTokenConverter.extractAuthentication(map);
        // 一些oauth2 資訊的填充
		OAuth2Request request = new OAuth2Request(parameters, clientId, authorities, true, scope, resourceIds, null, null,
				null);
		return new OAuth2Authentication(request, user);
	}
複製程式碼
  • 組裝當前使用者資訊
    DefaultUserAuthenticationConverter
public Authentication extractAuthentication(Map<String, ?> map) {
	if (map.containsKey(USERNAME)) {
		Object principal = map.get(USERNAME);
		Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
		if (userDetailsService != null) {
			UserDetails user = userDetailsService.loadUserByUsername((String) map.get(USERNAME));
			authorities = user.getAuthorities();
			principal = user;
		}
		return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
	}
	return null;
}
複製程式碼

問題分析

  • 認證伺服器check-token 返回的全部資訊
  • 資源伺服器在根據返回資訊組裝使用者資訊的時候,只是用了username
  • 如果設定了 userDetailsService 的實現則去呼叫 loadUserByUsername 再去查詢一次使用者資訊

造成問題現象

  1. 如果設定了userDetailsService 即可在spring security 上下文獲取使用者的全部資訊,不設定則只能得到使用者名稱。
  2. 增加了一次查詢邏輯,對效能產生不必要的影響

解決問題

  • 擴充套件UserAuthenticationConverter 的解析過程,把認證伺服器返回的資訊全部組裝到spring security的上下文物件中
/**
 * @author lengleng
 * @date 2019-03-07
 * <p>
 * 根據checktoken 的結果轉化使用者資訊
 */
public class PigxUserAuthenticationConverter implements UserAuthenticationConverter {
	private static final String N_A = "N/A";
    // map 是check-token 返回的全部資訊
	@Override
	public Authentication extractAuthentication(Map<String, ?> map) {
		if (map.containsKey(USERNAME)) {
			Collection<? extends GrantedAuthority> authorities = getAuthorities(map);

			String username = (String) map.get(USERNAME);
			Integer id = (Integer) map.get(SecurityConstants.DETAILS_USER_ID);
			Integer deptId = (Integer) map.get(SecurityConstants.DETAILS_DEPT_ID);
			Integer tenantId = (Integer) map.get(SecurityConstants.DETAILS_TENANT_ID);
			PigxUser user = new PigxUser(id, deptId, tenantId, username, N_A, true
					, true, true, true, authorities);
			return new UsernamePasswordAuthenticationToken(user, N_A, authorities);
		}
		return null;
	}
}
複製程式碼
  • 給remoteTokenServices 注入這個實現
public class PigxResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {

	@Override
	public void configure(ResourceServerSecurityConfigurer resources) {
		DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
		UserAuthenticationConverter userTokenConverter = new PigxUserAuthenticationConverter();
		accessTokenConverter.setUserTokenConverter(userTokenConverter);

		remoteTokenServices.setRestTemplate(lbRestTemplate);
		remoteTokenServices.setAccessTokenConverter(accessTokenConverter);
		resources.
				.tokenServices(remoteTokenServices);
	}
}

複製程式碼
  • 完成擴充套件,再來看文章開頭的流程圖就變成了如下

擴充套件資源伺服器解決oauth2 效能瓶頸

關注我

相關文章