2017-09-04 250 views
0

我有两个微服务。Feign客户端自定义oauth2响应

  • AUTH-服务(其使用弹簧安全的oauth2)
  • 属性服务

属性-微服务实现佯客户端,以便通过链路获取从在auth服务用户信息

/认证/用户/得/ {USER_ID}

property-microservice使用oauth2身份验证为了访问上面的auth服务端点(它工作正常,我可以得到响应)

但auth服务不会返回默认响应数据,因此假装客户端拦截器无法解析响应中的身份验证令牌。

需要明确的是,这是来自AUTH-服务的默认响应该弹簧提供:

{ 
    "access_token": "6e7519de-f211-47ca-afc0-b65ede51bdfc", 
    "token_type": "bearer", 
    "refresh_token": "6146216f-bedd-42bf-b4e5-95131b0c6380", 
    "expires_in": 7199, 
    "scope": "ui" 
} 

但我得到的回答是这样的:

{ 
    "code": 0, 
    "message": { 
     "type": "message", 
     "status": 200, 
     "result": 200, 
     "message": "Token aquired successfully." 
    }, 
    "data": { 
     "access_token": "6e7519de-f211-47ca-afc0-b65ede51bdfc", 
     "token_type": "bearer", 
     "refresh_token": "6146216f-bedd-42bf-b4e5-95131b0c6380", 
     "expires_in": 7199, 
     "scope": "ui" 
    } 
} 

因此,fiegn客户端查找的标准响应数据,并且由于我所做的修改而无法找到它。如果只有我可以覆盖ResponseExtractor里面OAuth2AccessTokenSupport类我可以正确解析响应。我如何管理从假装客户端解析自定义oauth2响应(或者是否有任何其他解决方案)?

Application.java(属性服务)

// For jsr310 java 8 java.time.* support for JPA 
@EntityScan(basePackageClasses = {Application.class, Jsr310JpaConverters.class}) 
@SpringBootApplication 
@EnableResourceServer 
@EnableOAuth2Client 
@EnableFeignClients 
@EnableHystrix 
@EnableGlobalMethodSecurity(prePostEnabled = true) 
@EnableConfigurationProperties 
@Configuration 
@EnableAutoConfiguration 
public class Application extends ResourceServerConfigurerAdapter { 
    @Autowired 
    private ResourceServerProperties sso; 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 

     HystrixDummy.start(); 
    } 

    @Bean 
    @ConfigurationProperties(prefix = "security.oauth2.client") 
    public ClientCredentialsResourceDetails clientCredentialsResourceDetails() { 
     return new ClientCredentialsResourceDetails(); 
    } 

    @Bean 
    public RequestInterceptor oauth2FeignRequestInterceptor() { 
     return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), clientCredentialsResourceDetails()); 
    } 

    @Bean 
    public OAuth2RestTemplate clientCredentialsRestTemplate() { 
     return new OAuth2RestTemplate(clientCredentialsResourceDetails()); 
    } 

    @Bean 
    public ResourceServerTokenServices tokenServices() { 
     return new CustomUserInfoTokenServices(this.sso.getUserInfoUri(), this.sso.getClientId()); 
    } 
} 

AuthServiceClient(属性服务)

@FeignClient(name = "auth-service", fallbackFactory = AuthServiceClient.AuthServiceClientFallback.class) 
public interface AuthServiceClient { 
    @RequestMapping(path = "/auth/users/get/{userId}", method = RequestMethod.GET) 
    RestResponse get(@PathVariable(value = "userId") Long userId); 

    @Component 
    class AuthServiceClientFallback implements FallbackFactory<AuthServiceClient> { 
     @Override 
     public AuthServiceClient create(Throwable cause) { 
      return userId -> new RestResponse(null, AppConstant.CODE_FAILURE, null); 
     } 
    } 
} 

Application.java(AUTH-服务)

@SpringBootApplication 
@EnableResourceServer 
@EnableGlobalMethodSecurity(prePostEnabled = true) 
public class Application { 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 
} 

UserController.java(验证服务)

@RestController 
@RequestMapping("/users") 
public class UserController { 
    @Autowired 
    private UserService userService; 

    @PreAuthorize("#oauth2.hasScope('server')") 
    @RequestMapping(value = "/get/{userId}", method = RequestMethod.GET) 
    public ResponseEntity<RestResponse> get(@Valid @PathVariable Long userId) throws UserNotFoundException { 
     User user = this.userService.findOne(userId); 

     RestResponse response = new RestResponse(); 

     RestMessage message = new RestMessage(); 
     message.setMessage(AppConstant.MESSAGE_USER_FETCHED_SUCCESS); 
     message.setResult(AppConstant.CODE_USER_FETCHED); 
     message.setStatus(HttpStatus.OK.value()); 

     response.setCode(AppConstant.CODE_SUCCESS); 
     response.setMessage(message); 
     response.setData(user); 

     return new ResponseEntity<>(response, HttpStatus.OK); 
    } 
} 

回答

0

我刚刚结束了编写自定义FeignClientRequestInterceptorFeignClientAccessTokenProvider这样的:

FeignClientAccessTokenProvider.java

public class FeignClientAccessTokenProvider extends ClientCredentialsAccessTokenProvider { 
    private ObjectMapper mapper = new ObjectMapper(); 

    @Override 
    protected OAuth2AccessToken retrieveToken(AccessTokenRequest request, OAuth2ProtectedResourceDetails resource, MultiValueMap<String, String> form, HttpHeaders headers) throws OAuth2AccessDeniedException { 
     OAuth2AccessToken token = super.retrieveToken(request, resource, form, headers); 

     if (token != null && token.getValue() == null && token.getAdditionalInformation() != null) { 
      if (token.getAdditionalInformation().containsKey("data")) { 
       token = this.mapper.convertValue(token.getAdditionalInformation().get("data"), OAuth2AccessToken.class); 
      } 
     } 

     return token; 
    } 
} 

FeignClientRequestInterceptor。java的

public class FeignClientRequestInterceptor implements RequestInterceptor { 

    public static final String BEARER = "Bearer"; 

    public static final String AUTHORIZATION = "Authorization"; 

    private final OAuth2ClientContext oAuth2ClientContext; 

    private final OAuth2ProtectedResourceDetails resource; 

    private final String tokenType; 

    private final String header; 

    private AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(Arrays 
      .<AccessTokenProvider>asList(new AuthorizationCodeAccessTokenProvider(), 
        new ImplicitAccessTokenProvider(), 
        new ResourceOwnerPasswordAccessTokenProvider(), 
        new FeignClientAccessTokenProvider())); 

    /** 
    * Default constructor which uses the provided OAuth2ClientContext and Bearer tokens 
    * within Authorization header 
    * 
    * @param oAuth2ClientContext provided context 
    * @param resource   type of resource to be accessed 
    */ 
    public FeignClientRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, 
             OAuth2ProtectedResourceDetails resource) { 
     this(oAuth2ClientContext, resource, BEARER, AUTHORIZATION); 
    } 

    /** 
    * Fully customizable constructor for changing token type and header name, in cases of 
    * Bearer and Authorization is not the default such as "bearer", "authorization" 
    * 
    * @param oAuth2ClientContext current oAuth2 Context 
    * @param resource   type of resource to be accessed 
    * @param tokenType   type of token e.g. "token", "Bearer" 
    * @param header    name of the header e.g. "Authorization", "authorization" 
    */ 
    public FeignClientRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, 
             OAuth2ProtectedResourceDetails resource, String tokenType, String header) { 
     this.oAuth2ClientContext = oAuth2ClientContext; 
     this.resource = resource; 
     this.tokenType = tokenType; 
     this.header = header; 
    } 

    /** 
    * Create a template with the header of provided name and extracted extract 
    * 
    * @see RequestInterceptor#apply(RequestTemplate) 
    */ 
    @Override 
    public void apply(RequestTemplate template) { 
     template.header(this.header, extract(this.tokenType)); 
    } 

    /** 
    * Extracts the token extract id the access token exists or returning an empty extract 
    * if there is no one on the context it may occasionally causes Unauthorized response 
    * since the token extract is empty 
    * 
    * @param tokenType type name of token 
    * @return token value from context if it exists otherwise empty String 
    */ 
    protected String extract(String tokenType) { 
     OAuth2AccessToken accessToken = getToken(); 
     return String.format("%s %s", tokenType, accessToken.getValue()); 
    } 

    /** 
    * Extract the access token within the request or try to acquire a new one by 
    * delegating it to {@link #acquireAccessToken()} 
    * 
    * @return valid token 
    */ 
    public OAuth2AccessToken getToken() { 

     OAuth2AccessToken accessToken = this.oAuth2ClientContext.getAccessToken(); 
     if (accessToken == null || accessToken.isExpired()) { 
      try { 
       accessToken = acquireAccessToken(); 
      } catch (UserRedirectRequiredException e) { 
       this.oAuth2ClientContext.setAccessToken(null); 
       String stateKey = e.getStateKey(); 
       if (stateKey != null) { 
        Object stateToPreserve = e.getStateToPreserve(); 
        if (stateToPreserve == null) { 
         stateToPreserve = "NONE"; 
        } 
        this.oAuth2ClientContext.setPreservedState(stateKey, stateToPreserve); 
       } 
       throw e; 
      } 
     } 
     return accessToken; 
    } 

    /** 
    * Try to acquire the token using a access token provider 
    * 
    * @return valid access token 
    * @throws UserRedirectRequiredException in case the user needs to be redirected to an 
    *          approval page or login page 
    */ 
    protected OAuth2AccessToken acquireAccessToken() 
      throws UserRedirectRequiredException { 
     AccessTokenRequest tokenRequest = this.oAuth2ClientContext.getAccessTokenRequest(); 
     if (tokenRequest == null) { 
      throw new AccessTokenRequiredException(
        "Cannot find valid context on request for resource '" 
          + this.resource.getId() + "'.", 
        this.resource); 
     } 
     String stateKey = tokenRequest.getStateKey(); 
     if (stateKey != null) { 
      tokenRequest.setPreservedState(
        this.oAuth2ClientContext.removePreservedState(stateKey)); 
     } 
     OAuth2AccessToken existingToken = this.oAuth2ClientContext.getAccessToken(); 
     if (existingToken != null) { 
      this.oAuth2ClientContext.setAccessToken(existingToken); 
     } 
     OAuth2AccessToken obtainableAccessToken; 
     obtainableAccessToken = this.accessTokenProvider.obtainAccessToken(this.resource, 
       tokenRequest); 
     if (obtainableAccessToken == null || obtainableAccessToken.getValue() == null) { 
      throw new IllegalStateException(
        " Access token provider returned a null token, which is illegal according to the contract."); 
     } 
     this.oAuth2ClientContext.setAccessToken(obtainableAccessToken); 
     return obtainableAccessToken; 
    } 
} 

希望这有助于任何人面临这个问题。