spring security oauth2 授權伺服器負載均衡解決方案
1.引言
本文所講的內容是以spring security oauth2 與 spring cloud 為基礎的,在微服務的構建中,將授權伺服器註冊在eureka上面。當客戶端訪問資源伺服器的時候必須攜帶token進行認證和授權。如果我們在資源伺服器上直接硬編碼授權伺服器地址,那麼我們是不能夠搭載高負載的授權伺服器的,所以我們需要一個可以負載均衡的RestTemplate進行負載呼叫授權伺服器。
2.建立負載均衡的RestTemplate
首先從原始碼檢視授權伺服器所使用的RestTemplate,可以發現資源伺服器的RemoteTokenServices類中是直接建立一個RestTemplate並且設定了異常處理器進行異常處理(關鍵),但是這明顯不是負載均衡的RestTemplate,所以我們需要重新設定一個負載均衡的RestTemplate
public class RemoteTokenServices implements ResourceServerTokenServices { ... private RestOperations restTemplate; public RemoteTokenServices() { restTemplate = new RestTemplate(); ((RestTemplate) restTemplate).setErrorHandler(new DefaultResponseErrorHandler() { @Override // Ignore 400 public void handleError(ClientHttpResponse response) throws IOException { if (response.getRawStatusCode() != 400) { super.handleError(response); } } }); } ... }
首先定義一個負載均衡的RestTemplate(加上@LoadBalanced)
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
之後將這個負載均衡的RestTemplate設定到剛剛的RemoteTokenServices類中,記得將原始碼中的異常處理器也設定進去,否則當認證失敗授權伺服器返回400錯誤的時候,restTemplate將會丟擲異常,如果沒有異常處理器,那麼此異常會被處理成500錯誤,這將難以使我們處理此錯誤。
@EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public abstract class ResServerConfig extends ResourceServerConfigurerAdapter { ... @Autowired(required = true) private RemoteTokenServices remoteTokenServices; @Autowired RestTemplate restTemplate; @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { super.configure(resources); if (StringUtils.isEmpty(oAuth2ClientProperties.getClientId())) { resources.resourceId(oAuth2ClientProperties.getClientId()); } restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { @Override // Ignore 400 public void handleError(ClientHttpResponse response) throws IOException { if (response.getRawStatusCode() != 400) { super.handleError(response); } } }); if (Objects.nonNull(remoteTokenServices)) { remoteTokenServices.setRestTemplate(restTemplate); resources.tokenServices(remoteTokenServices); } } ... }
3.在配置檔案設定服務名稱+端點地址
llg-oauth-server則是授權伺服器在eureka註冊的服務名稱
security.oauth2.client.client-id=resource1
security.oauth2.client.client-secret=0
security.oauth2.client.user-authorization-uri=http://llg-oauth-server:9015/oauth/authorize
security.oauth2.client.grant-type=password
security.oauth2.client.scope=read
security.oauth2.client.access-token-uri=http://llg-oauth-server:9015/oauth/token
#需要jwt儲存token的時候則需要配置下面的uri,拿到公共金鑰
#security.oauth2.resource.jwt.key-uri=http://localhost:9015/oauth/token_key
#使用remoteServiec的時候則需要配置chekc端點
security.oauth2.resource.token-info-uri=http://llg-oauth-server:9015/oauth/check_token
4.總結
此次問題主要是我之前在思考如何搭載負載均衡的授權伺服器的時候,只是重新set了一個負載均衡的RestTemplate,卻沒有仔細研究原始碼中對於異常處理器的重寫。過了一段時間當我想研究如何進行token過期自動續簽的時候,理想狀態是返回401錯誤給我,但是沒有想到的是返回了500錯誤,眾所周知500錯誤是服務端錯誤,範圍太廣不適合分析處理。所以當我追著異常研究1天之後發現一切都是RestTemplate異常沒有得到處理的緣故,最終導致此異常令spring mvc 返回500錯誤。所以在修改一些原始碼的時候切忌要弄清楚原始碼的流程和功能,不可盲目修改導致未知錯誤的發生。