我们之前做了一个consumer调用producer的接口,用的是feign,现在我们将OAuth2与Jwt加入了认证服务,此时使用feign是失败的,因为在请求到达consumer服务时,token被解析,调用feign时是一个新请求,此时请求是不带token的,在producer服务端会失败.
在feign调用时,我们是可以在请求发出时将token添加进去,
1.调用feign时将原本请求的token拿过来给feign使用
2.我们可以从客户端模式获取token,放入feign中
我这里是第二种方式::
我们定义myuaa客户端模式时,是将客户端模式加入了的,只需我们将客户端模式token加入,不论是谁,这个feign调用都可行,在spring中有一个请求拦截器RequestInterceptor,有一个security的实现OAuth2FeignRequestInterceptor,我们需要定义这个拦截器
定义一个feign配置类,方便管理区分其他的配置类
FeignConfiguration
@Configuration
public class FeignConfiguration {
@Bean
public RequestInterceptor getOAuth2RequestInterceptor() throws IOException {
return new OAuth2FeignRequestInterceptor();
}
}
进入发现OAuth2FeignRequestInterceptor源码
public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource) {
this(oAuth2ClientContext, resource, "Bearer", "Authorization");
}
参数OAuth2ClientContext,OAuth2ProtectedResourceDetails
OAuth2ClientContext是一个接口,有实现类DefaultOAuth2ClientContext,会有一个默认DefaultAccessTokenRequest请求类,加入拦截器参数
@Bean
public RequestInterceptor getOAuth2RequestInterceptor() throws IOException {
return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(),);
}
OAuth2ProtectedResourceDetails也是一个接口,实现类比较多,我们是准备用客户端模式,所以必填参数有grant_type与oauth/token的url,选择BaseOAuth2ProtectedResourceDetails的子类ClientCredentialsResourceDetails,这里需要注意一下,
整个流程说白了,就是创建一个RequestInterceptor,再重写的apply方法中对请求添加含有token的head
首先在OAuth2Properties中添加配置:
oauth2:
client-authorization:
access-token-uri: http://myuaa/oauth/token
token-service-id: myuaa //认证服务名
signature-verification:
public-key-endpoint-uri: http://myuaa/oauth/token_key
#ttl for public keys to verify JWT tokens (in ms)
ttl: 3600000
#max. rate at which public keys will be fetched (in ms)
public-key-refresh-rate-limit: 10000
web-client-configuration:
#keep in sync with UAA configuration
client-id: web_app
secret: changeit
修改配置bean OAuth2Properties
private final ClientAuthorization clientAuthorization = new ClientAuthorization();
public ClientAuthorization getClientAuthorization() {
return clientAuthorization;
}
public static class ClientAuthorization {
private String accessTokenUri;
private String tokenServiceId ;
public String getAccessTokenUri() {
return accessTokenUri;
}
public void setAccessTokenUri(String accessTokenUri) {
this.accessTokenUri = accessTokenUri;
}
public String getTokenServiceId() {
return tokenServiceId;
}
public void setTokenServiceId(String tokenServiceId) {
this.tokenServiceId = tokenServiceId;
}
}
在FeignConfiguration中注入
@Configuration
public class FeignConfiguration {
@Autowired
OAuth2Properties oAuth2Properties;
@Autowired
LoadBalancerClient loadBalancerClient;
public String getURL() { //获取token完整路径
if (oAuth2Properties.getClientAuthorization().getTokenServiceId().isEmpty()) {
try {
return loadBalancerClient.reconstructURI(
loadBalancerClient.choose(oAuth2Properties.getClientAuthorization().getTokenServiceId()),
new URI(oAuth2Properties.getClientAuthorization().getAccessTokenUri())
).toString();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
return oAuth2Properties.getClientAuthorization().getAccessTokenUri();
}
做一个ClientCredentialsResourceDetails的bean
@Bean
public ClientCredentialsResourceDetails loadClientCredentialsResourceDetails() {
ClientCredentialsResourceDetails clientCredentialsResourceDetails = new ClientCredentialsResourceDetails();
clientCredentialsResourceDetails.setAccessTokenUri(getURL());
clientCredentialsResourceDetails.setClientId(oAuth2Properties.getWebClientConfiguration().getClientId());
clientCredentialsResourceDetails.setClientSecret(oAuth2Properties.getWebClientConfiguration().getSecret());
return clientCredentialsResourceDetails;
}
@Bean
public RequestInterceptor getOAuth2RequestInterceptor() throws IOException {
return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(),loadClientCredentialsResourceDetails());
}
这里就完成了,
由于我的是用的OAuth2FeignRequestInterceptor拦截器,他里面实现了apply方法
public void apply(RequestTemplate template) {
template.header(this.header, new String[]{this.extract(this.tokenType)});
}
protected String extract(String tokenType) {
OAuth2AccessToken accessToken = this.getToken();
return String.format("%s %s", tokenType, accessToken.getValue());
}
在这里是不能用OAuth2ProtectedResourceDetails的实现类BaseOAuth2ProtectedResourceDetails,因为源码中获取token时会对Resource进行种类判断,必须为五种grant_type对应的类,
Iterator var3 = this.chain.iterator();
AccessTokenProvider tokenProvider;
do {
if (!var3.hasNext()) {
throw new OAuth2AccessDeniedException("Unable to obtain a new access token for resource '" + details.getId() + "'. The provider manager is not configured to support it.", details);
}
tokenProvider = (AccessTokenProvider)var3.next();
} while(!tokenProvider.supportsResource(details));
public class FeignOAuthRequestInterceptor implements RequestInterceptor {
@Autowired
private OAuth2RestTemplate oAuth2RestTemplate;
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header(
"Authorization",
String.format(
"%s %s",
"Bearer",
oAuth2RestTemplate.getAccessToken().toString()));
}
}
注入OAuth2RestTemplate,对他来一个实例化bean
@Configuration
public class Oauth2ClientConfig {
@Autowired
OAuth2Properties oAuth2Properties;
@Autowired
LoadBalancerClient loadBalancerClient;
public String getAccessTokenUri() {
if (!oAuth2Properties.getClientAuthorization().getTokenServiceId().isEmpty())
try {
String string = loadBalancerClient.reconstructURI(
loadBalancerClient.choose(oAuth2Properties.getClientAuthorization().getTokenServiceId()),
new URI(oAuth2Properties.getClientAuthorization().getAccessTokenUri())
).toString();
return string;
} catch (URISyntaxException e) {
e.printStackTrace();
}
return null;
}
@Bean
public ClientCredentialsResourceDetails resourceDetails() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
details.setAccessTokenUri(getAccessTokenUri());
details.setClientId(oAuth2Properties.getWebClientConfiguration().getClientId());
details.setClientSecret(oAuth2Properties.getWebClientConfiguration().getSecret());
return details;
}
@Bean
public OAuth2RestTemplate oAuth2RestTemplate() {
final OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resourceDetails(), new DefaultOAuth2ClientContext());
oAuth2RestTemplate.setRequestFactory(new SimpleClientHttpRequestFactory());//Netty4ClientHttpRequestFactory());//SimpleClientHttpRequestFactory
return oAuth2RestTemplate;
}
@Bean
public RequestInterceptor oauth2FeignRequestInterceptor(@Qualifier("paascloudOAuth2RestTemplate") OAuth2RestTemplate oAuth2RestTemplate) {
return new FeignOAuthRequestInterceptor();
}
}