Spring cloud 假装客户端自定义oauth2响应
我有两个微型服务Spring cloud 假装客户端自定义oauth2响应,spring-cloud,spring-security-oauth2,netflix-feign,Spring Cloud,Spring Security Oauth2,Netflix Feign,我有两个微型服务 身份验证服务(使用spring-security-oauth2) 物业服务 property microservice实现一个外部客户端,以便通过链接从身份验证服务获取用户信息 /auth/users/get/{USER\u ID} property microservice使用oauth2身份验证来访问上面的身份验证服务端点(工作正常,我可以得到响应) 但身份验证服务不返回默认响应数据,因此,外国客户端侦听器无法从响应中解析身份验证令牌 需要说明的是,这是spring提供
- 身份验证服务(使用spring-security-oauth2)
- 物业服务
{
“访问令牌”:“6e7519de-f211-47ca-afc0-b65ede51bdfc”,
“令牌类型”:“承载者”,
“刷新令牌”:“6146216f-bedd-42bf-b4e5-95131b0c6380”,
“expires_in”:7199,
“范围”:“用户界面”
}
但我的回答是这样的:
{
“代码”:0,
“信息”:{
“类型”:“消息”,
“地位”:200,
“结果”:200,
“消息”:“已成功获取令牌。”
},
“数据”:{
“访问令牌”:“6e7519de-f211-47ca-afc0-b65ede51bdfc”,
“令牌类型”:“承载者”,
“刷新令牌”:“6146216f-bedd-42bf-b4e5-95131b0c6380”,
“expires_in”:7199,
“范围”:“用户界面”
}
}
因此,fiegn客户端查找标准响应数据,但由于我所做的修改而无法找到它。如果我能在OAuth2AccessTokenSupport类内部重写ResponseExtractor,我就能正确解析响应。如何管理解析来自外部客户端的自定义oauth2响应(或者是否有其他解决方案)
Application.java(物业服务)
AuthServiceClient(物业服务)
UserController.java(身份验证服务)
@RestController
@请求映射(“/users”)
公共类用户控制器{
@自动连线
私人用户服务;
@预授权(#oauth2.hasScope('server'))
@RequestMapping(value=“/get/{userId}”,method=RequestMethod.get)
public ResponseEntity get(@Valid@PathVariable Long userId)引发UserNotFoundException{
User User=this.userService.findOne(userId);
RestResponse response=新的RestResponse();
RestMessage=newrestmessage();
message.setMessage(AppConstant.message\u USER\u FETCHED\u SUCCESS);
message.setResult(AppConstant.CODE\u USER\u获取);
message.setStatus(HttpStatus.OK.value());
response.setCode(AppConstant.CODE_SUCCESS);
response.setMessage(message);
response.setData(用户);
返回新的ResponseEntity(response,HttpStatus.OK);
}
}
我刚刚结束了编写自定义假装客户端接收器和假装客户端访问令牌提供程序的过程,如下所示:
faignClientAccessTokenProvider.java
公共类FaignClientAccessTokenProvider扩展ClientCredentialAccessTokenProvider{
私有对象映射器映射器=新对象映射器();
@凌驾
受保护的OAuth2AccessToken retrieveToken(AccessTokenRequest请求、OAuth2ProtectedResourceDetails资源、多值映射表单、HttpHeaders标头)引发OAuth2AccessDeniedException{
OAuth2AccessToken token=super.retrieveToken(请求、资源、表单、头);
if(token!=null&&token.getValue()==null&&token.getAdditionalInformation()!=null){
if(token.getAdditionalInformation().containsKey(“数据”)){
token=this.mapper.convertValue(token.getAdditionalInformation().get(“数据”),OAuth2AccessToken.class);
}
}
返回令牌;
}
}
假装ClientRequestInterceptor.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;
}
}
公共类假ClientRequestInterceptor实现RequestInterceptor{
公共静态最终字符串BEARER=“BEARER”;
公共静态最终字符串AUTHORIZATION=“AUTHORIZATION”;
专用最终OAuth2ClientContext OAuth2ClientContext;
私有最终OAuth2受保护的资源详细信息资源;
私有最终字符串标记类型;
私有最终字符串头;
私有AccessTokenProvider AccessTokenProvider=新AccessTokenProviderChain(数组
.asList(新授权代码AccessTokenProvider(),
新隐式AccessTokenProvider(),
新建ResourceOwnerPasswordAccessTokenProvider(),
新的FaignClientAccessTokenProvider());
/**
*使用提供的OAuth2Client上下文和承载令牌的默认构造函数
*在授权标头内
*
*@param OAuth2Client上下文提供了上下文
*@param要访问的资源的资源类型
*/
public-faignClientRequestInterceptor(OAuth2ClientContext OAuth2ClientContext,
OAuth2ProtectedResourceDetails资源){
这(OAuth2Client上下文、资源、承载、授权);
}
/**
*完全可自定义的构造函数,用于在以下情况下更改令牌类型和头名称:
*承载和授权不是默认值,例如“承载”、“授权”
*
*@param OAuth2Client上下文当前oAuth2上下文
*@param要访问的资源的资源类型
*@param令牌类型令牌类型,例如“令牌”、“承载者”
*@param标头标头的名称,例如“Authorization”、“Authorization”
*/
public-faignClientRequestInterceptor(OAuth2ClientContext OAuth2ClientContext,
OAuth2ProtectedResourceDetails资源,字符串标记类型,字符串头){
this.oAuth2ClientContext=oAuth2ClientContext;
这个资源=资源;
this.tokenType=tokenType;
this.header=头;
}
/**
*创建一个标题为pr的模板
@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);
}
}
}
@SpringBootApplication
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@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);
}
}
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;
}
}
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;
}
}