Spring-security-oauth2的版本是2.2.3。
RemoteTokenServices是用于向远程认证服务器验证token,同时获取token对应的用户的信息。
图1
RemoteTokenServices会通过RestTemplate调用远程服务,我们在使用这个类时,要设置checkTokenEndpointUrl、clientId、clientSecret等。
RemoteTokenServices的源码如下,
List-1
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的合法性,之后返回其信息。
List-2
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方法来获取用户信息,
List-3
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
Reference:
- http://springcloud.cn/view/431
- Spring-security-oauth2的源码
来源:oschina
链接:https://my.oschina.net/u/2518341/blog/3021642