当前位置: 代码迷 >> 综合 >> Springsecurity-oauth2之RemoteTokenServices
  详细解决方案

Springsecurity-oauth2之RemoteTokenServices

热度:42   发布时间:2023-12-12 01:53:14.0

 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"})@ResponseBodypublic 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:

  1. http://springcloud.cn/view/431
  2. Spring-security-oauth2的源码
  相关解决方案