javascript
Spring Cloud Open Feign系列【23】OAuth2FeignRequestInterceptor、BasicAuthRequestInterceptor拦截器解析
有道無術(shù),術(shù)尚可求,有術(shù)無道,止于術(shù)。
資料整理來自網(wǎng)絡(luò)
文章目錄
- BasicAuthRequestInterceptor
- Basic 認證
- 使用案例
- 源碼
- OAuth2FeignRequestInterceptor
- 源碼分析
- 應用場景
- 使用案例
BasicAuthRequestInterceptor
BasicAuthRequestInterceptor翻譯過來就是 Basic 認證請求攔截器。
Basic 認證
Basic認證是一種較為簡單的HTTP認證方式,客戶端通過明文(Base64編碼格式)傳輸用戶名和密碼到服務端進行認證,通常需要配合HTTPS來保證信息傳輸?shù)陌踩?/p>
比如Security 就支持這種方式,在發(fā)送認證請求時,按照以下格式:
// 請求頭Authorization 添加Basic 認證信息 Authorization: Basic 用戶名:密碼Base64編碼格式使用案例
首先注入一個BasicAuthRequestInterceptor。
@BeanBasicAuthRequestInterceptor basicAuthRequestInterceptor(){return new BasicAuthRequestInterceptor("zhangsan","123456");}然后使用Feign調(diào)用遠程服務,可以在日志中看到在消息頭中添加了Basic認證信息:
源碼
源碼也很簡單,就是將用戶名密碼,經(jīng)過編碼后放入到消息頭中:
public class BasicAuthRequestInterceptor implements RequestInterceptor {private final String headerValue;public BasicAuthRequestInterceptor(String username, String password) {this(username, password, Util.ISO_8859_1);}public BasicAuthRequestInterceptor(String username, String password, Charset charset) {Util.checkNotNull(username, "username", new Object[0]);Util.checkNotNull(password, "password", new Object[0]);this.headerValue = "Basic " + base64Encode((username + ":" + password).getBytes(charset));}private static String base64Encode(byte[] bytes) {return Base64.encode(bytes);}public void apply(RequestTemplate template) {template.header("Authorization", new String[]{this.headerValue});} }OAuth2FeignRequestInterceptor
OAuth2FeignRequestInterceptor屬于spring-cloud-security包,可以看到都標記為了過時,這是因為security-oauth2已經(jīng)快停止維護,換了新的項目,已經(jīng)正式發(fā)布。
源碼分析
先按照順序看下這個攔截器的處理邏輯,可以看到就是使用一個OAuth2 客戶端上下文對象來存儲令牌信息,每次請求時,會去校驗當前上下文中令牌是否過期或不存在,不可用時,調(diào)用配置的認證中心獲取令牌,然后將令牌放入到請求頭中。
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {public static final String BEARER = "Bearer";public static final String AUTHORIZATION = "Authorization";private final OAuth2ClientContext oAuth2ClientContext;private final OAuth2ProtectedResourceDetails resource;private final String tokenType;private final String header;private AccessTokenProvider accessTokenProvider;public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource) {this(oAuth2ClientContext, resource, "Bearer", "Authorization");}public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource, String tokenType, String header) {this.accessTokenProvider = new AccessTokenProviderChain(Arrays.asList(new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider()));this.oAuth2ClientContext = oAuth2ClientContext;this.resource = resource;this.tokenType = tokenType;this.header = header;}public void apply(RequestTemplate template) {// 5. 將Oauth2 令牌放入到消息頭中。template.header(this.header, new String[0]);template.header(this.header, new String[]{this.extract(this.tokenType)});}protected String extract(String tokenType) {OAuth2AccessToken accessToken = this.getToken();return String.format("%s %s", tokenType, accessToken.getValue());}public OAuth2AccessToken getToken() {// 1. 獲取 OAuth2令牌 OAuth2AccessToken accessToken = this.oAuth2ClientContext.getAccessToken();if (accessToken == null || accessToken.isExpired()) {// 2. 如果令牌不存在或者過期,重新獲取一個令牌try {accessToken = this.acquireAccessToken();} catch (UserRedirectRequiredException var5) {this.oAuth2ClientContext.setAccessToken((OAuth2AccessToken)null);String stateKey = var5.getStateKey();if (stateKey != null) {Object stateToPreserve = var5.getStateToPreserve();if (stateToPreserve == null) {stateToPreserve = "NONE";}this.oAuth2ClientContext.setPreservedState(stateKey, stateToPreserve);}throw var5;}}return accessToken;}protected OAuth2AccessToken acquireAccessToken() throws UserRedirectRequiredException {// 3. 創(chuàng)建一個獲取令牌的請求對象AccessTokenRequest tokenRequest = this.oAuth2ClientContext.getAccessTokenRequest();if (tokenRequest == null) {throw new AccessTokenRequiredException("Cannot find valid context on request for resource '" + this.resource.getId() + "'.", this.resource);} else {String stateKey = tokenRequest.getStateKey();if (stateKey != null) {tokenRequest.setPreservedState(this.oAuth2ClientContext.removePreservedState(stateKey));}OAuth2AccessToken existingToken = this.oAuth2ClientContext.getAccessToken();if (existingToken != null) {this.oAuth2ClientContext.setAccessToken(existingToken);}// 4. 使用令牌提供者獲取一個令牌OAuth2AccessToken obtainableAccessToken = this.accessTokenProvider.obtainAccessToken(this.resource, tokenRequest);if (obtainableAccessToken != null && obtainableAccessToken.getValue() != null) {this.oAuth2ClientContext.setAccessToken(obtainableAccessToken);return obtainableAccessToken;} else {throw new IllegalStateException(" Access token provider returned a null token, which is illegal according to the contract.");}}}public void setAccessTokenProvider(AccessTokenProvider accessTokenProvider) {this.accessTokenProvider = accessTokenProvider;} }應用場景
當某個服務是Oauth 2的資源服務器,第三方使用Feign 去訪問時,需要攜帶Oauth 2令牌去訪問,這個時候就可以使用當前攔截器添加Oauth 2認證。
使用案例
場景:現(xiàn)在我們有一個微服務項目,采用Oauth 2認證方式,這個時候有個第三方,想通過Feign 調(diào)用我們的資源服務器,這個時候就可以讓第三方集成我們的 Oauth 2客戶端。
首先需要引入依賴:
然后注入 一個 Oauth 2客戶端,這里演示使用密碼模式。
@BeanOAuth2ClientContext oAuth2ClientContext(){return new DefaultOAuth2ClientContext();}@BeanOAuth2FeignRequestInterceptor oAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, ResourceOwnerPasswordResourceDetails resourceOwnerPasswordResourceDetails){return new OAuth2FeignRequestInterceptor(oAuth2ClientContext,resourceOwnerPasswordResourceDetails);}@BeanResourceOwnerPasswordResourceDetails resourceOwnerPasswordResourceDetails(){ResourceOwnerPasswordResourceDetails resourceOwnerPasswordResourceDetails = new ResourceOwnerPasswordResourceDetails();resourceOwnerPasswordResourceDetails.setUsername("hnmqet");resourceOwnerPasswordResourceDetails.setPassword("123456");resourceOwnerPasswordResourceDetails.setClientId("ZD");resourceOwnerPasswordResourceDetails.setClientSecret("123456");resourceOwnerPasswordResourceDetails.setAccessTokenUri("http://192.168.58.1:21101/oauth/token");return resourceOwnerPasswordResourceDetails;}測試,可以看到該攔截器,最終會調(diào)用OAuth2AccessTokenSupport去遠程獲取令牌,然后放在消息頭中去發(fā)送Feign 請求。
總結(jié)
以上是生活随笔為你收集整理的Spring Cloud Open Feign系列【23】OAuth2FeignRequestInterceptor、BasicAuthRequestInterceptor拦截器解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 全机房最蒟蒻的讲堂_第一期_关于orz
- 下一篇: 在win10本地开发springboot