Spring-security-oauth2的版本是2.2.3。java
RemoteTokenServices是用於向遠程認證服務器驗證token,同時獲取token對應的用戶的信息。spring
圖1服務器
RemoteTokenServices會經過RestTemplate調用遠程服務,咱們在使用這個類時,要設置checkTokenEndpointUrl、clientId、clientSecret等。app
RemoteTokenServices的源碼以下,ide
List-1post
public class RemoteTokenServices implements ResourceServerTokenServices { protected final Log logger = LogFactory.getLog(this.getClass()); private RestOperations restTemplate = new RestTemplate(); private String checkTokenEndpointUrl; private String clientId; private String clientSecret; private String tokenName = "token"; private AccessTokenConverter tokenConverter = new DefaultAccessTokenConverter(); public RemoteTokenServices() { ((RestTemplate)this.restTemplate).setErrorHandler(new DefaultResponseErrorHandler() { public void handleError(ClientHttpResponse response) throws IOException { if (response.getRawStatusCode() != 400) { super.handleError(response); } } }); } public void setRestTemplate(RestOperations restTemplate) { this.restTemplate = restTemplate; } public void setCheckTokenEndpointUrl(String checkTokenEndpointUrl) { this.checkTokenEndpointUrl = checkTokenEndpointUrl; } public void setClientId(String clientId) { this.clientId = clientId; } public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; } public void setAccessTokenConverter(AccessTokenConverter accessTokenConverter) { this.tokenConverter = accessTokenConverter; } public void setTokenName(String tokenName) { this.tokenName = tokenName; } public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { MultiValueMap<String, String> formData = new LinkedMultiValueMap(); formData.add(this.tokenName, accessToken); HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", this.getAuthorizationHeader(this.clientId, this.clientSecret)); Map<String, Object> map = this.postForMap(this.checkTokenEndpointUrl, formData, headers); if (map.containsKey("error")) { this.logger.debug("check_token returned error: " + map.get("error")); throw new InvalidTokenException(accessToken); } else if (!Boolean.TRUE.equals(map.get("active"))) { this.logger.debug("check_token returned active attribute: " + map.get("active")); throw new InvalidTokenException(accessToken); } else { return this.tokenConverter.extractAuthentication(map); } } public OAuth2AccessToken readAccessToken(String accessToken) { throw new UnsupportedOperationException("Not supported: read access token"); } private Stri』g getAuthorizationHeader(String clientId, String clientSecret) { if (clientId == null || clientSecret == null) { this.logger.warn("Null Client ID or Client Secret detected. Endpoint that requires authentication will reject request with 401 error."); } String creds = String.format("%s:%s", clientId, clientSecret); try { return "Basic " + new String(Base64.encode(creds.getBytes("UTF-8"))); } catch (UnsupportedEncodingException var5) { throw new IllegalStateException("Could not convert String"); } } private Map<String, Object> postForMap(String path, MultiValueMap<String, String> formData, HttpHeaders headers) { if (headers.getContentType() == null) { headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); } Map map = (Map)this.restTemplate.exchange(path, HttpMethod.POST, new HttpEntity(formData, headers), Map.class, new Object[0]).getBody(); return map; } }
圖1的步驟4中,請求/oauth/check_token,以下List-2,查詢token的合法性,以後返回其信息。ui
List-2this
package org.springframework.security.oauth2.provider.endpoint; @FrameworkEndpoint public class CheckTokenEndpoint { private ResourceServerTokenServices resourceServerTokenServices; private AccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); protected final Log logger = LogFactory.getLog(this.getClass()); private WebResponseExceptionTranslator exceptionTranslator = new DefaultWebResponseExceptionTranslator(); public CheckTokenEndpoint(ResourceServerTokenServices resourceServerTokenServices) { this.resourceServerTokenServices = resourceServerTokenServices; } public void setExceptionTranslator(WebResponseExceptionTranslator exceptionTranslator) { this.exceptionTranslator = exceptionTranslator; } public void setAccessTokenConverter(AccessTokenConverter accessTokenConverter) { this.accessTokenConverter = accessTokenConverter; } @RequestMapping({"/oauth/check_token"}) @ResponseBody public Map<String, ?> checkToken(@RequestParam("token") String value) { OAuth2AccessToken token = this.resourceServerTokenServices.readAccessToken(value); if (token == null) { throw new InvalidTokenException("Token was not recognised"); } else if (token.isExpired()) { throw new InvalidTokenException("Token has expired"); } else { OAuth2Authentication authentication = this.resourceServerTokenServices.loadAuthentication(token.getValue()); Map<String, ?> response = this.accessTokenConverter.convertAccessToken(token, authentication); return response; } } ......
圖1中的步驟8中,若是UserDetailsService不爲空,那麼會調用它的loadUserByUsername方法來獲取用戶信息,spa
List-3debug
public class DefaultUserAuthenticationConverter implements UserAuthenticationConverter { ...... public Authentication extractAuthentication(Map<String, ?> map) { if (map.containsKey("user_name")) { Object principal = map.get("user_name"); Collection<? extends GrantedAuthority> authorities = this.getAuthorities(map); if (this.userDetailsService != null) { UserDetails user = this.userDetailsService.loadUserByUsername((String)map.get("user_name")); authorities = user.getAuthorities(); principal = user; } return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities); } else { return null; } } ......
建議讀者閱讀源碼。
在哪裏使用到RemoteTokenServices呢,看以下圖2,OAuth2AuthenticationProcessingFilter會攔截請求,將access_token轉換爲用戶信息。
圖2