resource-server(資源服務器)
資源服務器
- 要訪問資源服務器受保護的資源需要攜帶令牌(從授權服務器獲得)
- 客戶端往往同時也是一個資源服務器,各個服務之間的通信(訪問需要權限的資源)時需攜帶訪問令牌
- 資源服務器通過 @EnableResourceServer 注解來開啟一個 OAuth2AuthenticationProcessingFilter 類型的過濾器
- 通過繼承 ResourceServerConfigurerAdapter 類來配置資源服務器
ResourceServerProperties
- OAuth2 為資源服務器配置提供了 ResourceServerProperties 類,該類會讀取配置文件中對資源服務器得配置信息(如授權服務器公鑰訪問地址)
ResourceServerSecurityConfigurer 可配置屬性
- tokenServices:ResourceServerTokenServices 類的實例,用來實現令牌業務邏輯服務
- resourceId:這個資源服務的ID,這個屬性是可選的,但是推薦設置並在授權服務中進行驗證
- tokenExtractor 令牌提取器用來提取請求中的令牌
- 請求匹配器,用來設置需要進行保護的資源路徑,默認的情況下是受保護資源服務的全部路徑
- 受保護資源的訪問規則,默認的規則是簡單的身份驗證(plain authenticated)
- 其他的自定義權限保護規則通過 HttpSecurity 來進行配置
解析令牌方法:
- 使用 DefaultTokenServices 在資源服務器本地配置令牌存儲、解碼、解析方式
- 使用 RemoteTokenServices 資源服務器通過 HTTP 請求來解碼令牌,每次都請求授權服務器端點 /oauth/check_token
- 若授權服務器是 JWT 非對稱加密,則需要請求授權服務器的 /oauth/token_key 來獲取公鑰 key 進行解碼
代碼案例
令牌解析(JWT 對稱加密)
資源服務器和授權服務器不在同一個應用,則需告訴資源服務器令牌如何存儲與解析,並與授權服務器使用相同的密鑰進行解密
@Configuration @EnableResourceServer public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter{ @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } //與授權服務器使用共同的密鑰進行解析 @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("123"); return converter; } }
令牌解析(JWT 非對稱加密)
- 非對稱加密需要公鑰,可以從本地獲取,也可以從授權服務器提供的公鑰端點獲取
- 若本地獲取不到公鑰資源文件 pubkey.txt 則從授權服務器端點獲取
@Configuration @EnableResourceServer public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter { @Autowired private ResourceServerProperties resourceServerProperties; @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); //設置用於解碼的非對稱加密的公鑰 converter.setVerifierKey(getPubKey()); return converter; } private String getPubKey() { Resource resource = new ClassPathResource("pubkey.txt"); try (BufferedReader br = new BufferedReader(new InputStreamReader(resource.getInputStream()))) { System.out.println("本地公鑰"); return br.lines().collect(Collectors.joining("\n")); } catch (IOException ioe) { return getKeyFromAuthorizationServer(); } } private String getKeyFromAuthorizationServer() { ObjectMapper objectMapper = new ObjectMapper(); String pubKey = new RestTemplate().getForObject(resourceServerProperties.getJwt().getKeyUri(), String.class); try { Map map = objectMapper.readValue(pubKey, Map.class); System.out.println("聯網公鑰"); return map.get("value").toString(); } catch (IOException e) { e.printStackTrace(); } return null; } }
令牌解析(通過訪問授權服務器解析令牌-適用 JDBC、內存存儲)
- 資源服務器通過訪問授權服務器 /oauth/check_token 端點解析令牌需要使用 RemoteTokenServices
- 並且使用 DefaultAccessTokenConverter 來實現令牌數據的存儲
@Autowired private OAuth2ClientProperties oAuth2ClientProperties; @Autowired private AuthorizationServerProperties authorizationServerProperties; @Bean public ResourceServerTokenServices tokenServices() { RemoteTokenServices remoteTokenServices = new RemoteTokenServices(); remoteTokenServices.setCheckTokenEndpointUrl(authorizationServerProperties.getCheckTokenAccess()); remoteTokenServices.setClientId(oAuth2ClientProperties.getClientId()); remoteTokenServices.setClientSecret(oAuth2ClientProperties.getClientSecret()); remoteTokenServices.setAccessTokenConverter(accessTokenConverter()); return remoteTokenServices; } @Bean public AccessTokenConverter accessTokenConverter() { return new DefaultAccessTokenConverter(); }
- 修改配置文件
security: oauth2: client: clientId: resource1 clientSecret: secret userAuthorizationUri: http://localhost:9005/oauth/authorize grant-type: password scope: read access-token-uri: http://localhost:9005/oauth/token resource: userInfoUri: http://localhost:9005/user authorization: check-token-access: http://localhost:9005/oauth/check_token basic: enabled: false
