springcloud中feign调用的权限认证

springcloud中feign调用的权限认证

我们之前做了一个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));

2.也可以直接使用自定义类来实现RequestInterceptor,自己写

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();
    }
}

你可能感兴趣的:(springcloud微服务)