spring cloud oauth2 feign 遇到的坑及解决

目录
  • springcloudoauth2feign遇到的坑
    • 客户端模式
    • 基于springsecurity
  • springcloud微服务增加oauth2权限后feign调用报null
    • 一般是这样实现的

spring cloud oauth2 feign 遇到的坑

关于oauth2相关的内容这里不重复描述,在spring cloud中在管理内部api时鉴权相信有很多人会有疑问,这里描述两种比较low的用法,由于公司内部使用的是阿里云edas这里仅仅是记录一下,如果有更好的用法在请赐教,不喜勿喷!

客户端模式

提供三方jar包

这里需要抽一个jar包,需要用到feign的为服务端直接利用maven模式引入feign即可

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.netflix.feign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <version>8.18.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
    </dependencies>

核心类

  • CustomHystrixConcurrencyStrategy.java
  • Oauth2ClientProperties.java
  • OAuth2FeignAutoConfiguration.java
  • OAuth2FeignRequestInterceptor.java
package com.paascloud.security.feign;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * The class Oauth 2 client properties.
 *
 * @author paascloud.net @gmail.com
 */
@Data
@ConfigurationProperties(prefix = "paascloud.oauth2.client")
public class Oauth2ClientProperties {
    private String id;
    private String accessTokenUrl;
    private String clientId;
    private String clientSecret;
    private String clientAuthenticationScheme;
}
package com.paascloud.security.feign;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import org.springframework.stereotype.Component;
import java.util.concurrent.Callable;
/**
 * The class Custom hystrix concurrency strategy.
 *
 * @author paascloud.net @gmail.com
 */
@Component
public class CustomHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
    /**
     * Instantiates a new Custom hystrix concurrency strategy.
     */
    public CustomHystrixConcurrencyStrategy() {
        HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
    }
    /**
     * Wrap callable callable.
     *
     * @param <T>      the type parameter
     * @param callable the callable
     *
     * @return the callable
     */
    @Override
    public <T> Callable<T> wrapCallable(Callable<T> callable) {
        return new HystrixContextWrapper<T>(callable);
    }
    /**
     * The class Hystrix context wrapper.
     *
     * @param <V> the type parameter
     *
     * @author paascloud.net @gmail.com
     */
    public static class HystrixContextWrapper<V> implements Callable<V> {
        private HystrixRequestContext hystrixRequestContext;
        private Callable<V> delegate;
        /**
         * Instantiates a new Hystrix context wrapper.
         *
         * @param delegate the delegate
         */
        HystrixContextWrapper(Callable<V> delegate) {
        this.hystrixRequestContext = HystrixRequestContext.getContextForCurrentThread();
            this.delegate = delegate;
        }
        /**
         * Call v.
         *
         * @return the v
         *
         * @throws Exception the exception
         */
        @Override
        public V call() throws Exception {
            HystrixRequestContext existingState = HystrixRequestContext.getContextForCurrentThread();
            try {
                HystrixRequestContext.setContextOnCurrentThread(this.hystrixRequestContext);
                return this.delegate.call();
            } finally {
                HystrixRequestContext.setContextOnCurrentThread(existingState);
            }
        }
    }
}
package com.paascloud.security.feign;
import feign.Logger;
import feign.RequestInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.security.oauth2.common.AuthenticationScheme;
/**
 * The class O auth 2 feign auto configuration.
 *
 * @author paascloud.net @gmail.com
 */
@Configuration
@EnableConfigurationProperties(Oauth2ClientProperties.class)
public class OAuth2FeignAutoConfiguration {
    private final Oauth2ClientProperties oauth2ClientProperties;
    /**
     * Instantiates a new O auth 2 feign auto configuration.
     *
     * @param oauth2ClientProperties the oauth 2 client properties
     */
    @Autowired
    public OAuth2FeignAutoConfiguration(Oauth2ClientProperties oauth2ClientProperties) {
        this.oauth2ClientProperties = oauth2ClientProperties;
    }
    /**
     * Resource details client credentials resource details.
     *
     * @return the client credentials resource details
     */
    @Bean("paascloudClientCredentialsResourceDetails")
    public ClientCredentialsResourceDetails resourceDetails() {
        ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
        details.setId(oauth2ClientProperties.getId());
        details.setAccessTokenUri(oauth2ClientProperties.getAccessTokenUrl());
        details.setClientId(oauth2ClientProperties.getClientId());
        details.setClientSecret(oauth2ClientProperties.getClientSecret());
        details.setAuthenticationScheme(AuthenticationScheme.valueOf(oauth2ClientProperties.getClientAuthenticationScheme()));
        return details;
    }
    /**
     * O auth 2 rest template o auth 2 rest template.
     *
     * @return the o auth 2 rest template
     */
    @Bean("paascloudOAuth2RestTemplate")
    public OAuth2RestTemplate oAuth2RestTemplate() {
        final OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resourceDetails(), new DefaultOAuth2ClientContext());
        oAuth2RestTemplate.setRequestFactory(new Netty4ClientHttpRequestFactory());
        return oAuth2RestTemplate;
    }
    /**
     * Oauth 2 feign request interceptor request interceptor.
     *
     * @param oAuth2RestTemplate the o auth 2 rest template
     *
     * @return the request interceptor
     */
    @Bean
    public RequestInterceptor oauth2FeignRequestInterceptor(@Qualifier("paascloudOAuth2RestTemplate") OAuth2RestTemplate oAuth2RestTemplate) {
        return new OAuth2FeignRequestInterceptor(oAuth2RestTemplate);
    }
    /**
     * Feign logger level logger . level.
     *
     * @return the logger . level
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}
package com.paascloud.security.feign;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.util.Assert;
/**
 * The class O auth 2 feign request interceptor.
 *
 * @author paascloud.net @gmail.com
 */
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String BEARER_TOKEN_TYPE = "bearer";
    private final OAuth2RestTemplate oAuth2RestTemplate;
    /**
     * Instantiates a new O auth 2 feign request interceptor.
     *
     * @param oAuth2RestTemplate the o auth 2 rest template
     */
    OAuth2FeignRequestInterceptor(OAuth2RestTemplate oAuth2RestTemplate) {
        Assert.notNull(oAuth2RestTemplate, "Context can not be null");
        this.oAuth2RestTemplate = oAuth2RestTemplate;
    }
    /**
     * Apply.
     *
     * @param template the template
     */
    @Override
    public void apply(RequestTemplate template) {
        template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE,  oAuth2RestTemplate.getAccessToken().toString()));
    }
}

调用端配置

引入maven依赖

<dependency>
            <groupId>com.liuzm.paascloud.common</groupId>
            <artifactId>paascloud-security-feign</artifactId>
            <version>1.0-SNAPSHOT</version>
</dependency>

@FeignClient加入configuration属性

/**
 * The interface Mdc product feign api.
 * @author paascloud.net@gmail.com
 */
@FeignClient(value = "paascloud-provider-mdc", configuration = OAuth2FeignAutoConfiguration.class, fallback = MdcProductFeignHystrix.class)
public interface MdcProductFeignApi {
    /**
     * Update product stock by id int.
     *
     * @param productDto the product dto
     *
     * @return the int
     */
    @RequestMapping(value = "/api/product/updateProductStockById", method = RequestMethod.POST)
    int updateProductStockById(@RequestBody ProductDto productDto);
}

认证服务器配置

@Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(restClientDetailsService);
    }
package com.paascloud.provider.security;
import com.paascloud.security.core.properties.OAuth2ClientProperties;
import com.paascloud.security.core.properties.SecurityProperties;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
 * The class Rest client details service.
 *
 * @author paascloud.net @gmail.com
 */
@Component("restClientDetailsService")
public class RestClientDetailsServiceImpl implements ClientDetailsService {
    private ClientDetailsService clientDetailsService;
    @Autowired
    private SecurityProperties securityProperties;
    /**
     * Init.
     */
    @PostConstruct
    public void init() {
        InMemoryClientDetailsServiceBuilder builder = new InMemoryClientDetailsServiceBuilder();
        if (ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) {
            for (OAuth2ClientProperties client : securityProperties.getOauth2().getClients()) {
                builder.withClient(client.getClientId())
                        .secret(client.getClientSecret())
                        .authorizedGrantTypes("refresh_token", "password", "client_credentials")
                        .accessTokenValiditySeconds(client.getAccessTokenValidateSeconds())
                        .refreshTokenValiditySeconds(2592000)
                        .scopes(client.getScope());
            }
        }
        try {
            clientDetailsService = builder.build();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * Load client by client id client details.
     *
     * @param clientId the client id
     *
     * @return the client details
     *
     * @throws ClientRegistrationException the client registration exception
     */
    @Override
    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
        return  clientDetailsService.loadClientByClientId(clientId);
    }
}

bootstrap.yml配置

security:
    oauth2:
      tokenStore: jwt
      clients[0]:
        clientId: paascloud-client-uac
        clientSecret: paascloudClientSecret
        accessTokenValidateSeconds: 7200
        scope: "*"
      clients[1]:
        clientId: paascloud-browser
        clientSecret: paascloudClientSecret
        accessTokenValidateSeconds: 7200
        scope: "*"
      clients[2]:
        clientId: paascloud-client-gateway
        clientSecret: paascloudClientSecret
        accessTokenValidateSeconds: 7200
        scope: "*"
      clients[3]:
        clientId: paascloud-client-zipkin
        clientSecret: paascloudClientSecret
        accessTokenValidateSeconds: 7200
        scope: "*"
      clients[4]:
        clientId: paascloud-client-mdc
        clientSecret: paascloudClientSecret
        accessTokenValidateSeconds: 7200
        scope: "*"
      clients[5]:
        clientId: paascloud-client-omc
        clientSecret: paascloudClientSecret
        accessTokenValidateSeconds: 7200
        scope: "*"
      clients[6]:
        clientId: paascloud-client-opc
        clientSecret: paascloudClientSecret
        accessTokenValidateSeconds: 7200
        scope: "*"

到此客户端模式配置完成!

基于spring security

开放权限,利用url规范来规划客户端的url不通过auth2鉴权,这里唯一的区别是在feign拦截器里处理的逻辑改一下,代码如下

@Autowired
private OAuth2ClientContext context;
@Override
    public void apply(RequestTemplate template) {
        if(context.getAccessToken() != null && context.getAccessToken().getValue() != null && OAuth2AccessToken.BEARER_TYPE.equalsIgnoreCase(context.getAccessToken().getTokenType()) ){
            template.header("Authorization", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, context.getAccessToken().getValue()));
        }
    }

spring cloud微服务增加oauth2权限后 feign调用报null

在授权服务里,用户通过用户名密码,或者手机和验证码等方式登陆之后,在http头里会有授权的标识,在客户端调用时,需要添加当时有效的token才可以正常访问被授权的页面。

Content-Type:application/json
Authorization:Bearer d79c064c-8675-4047-a119-fac692e447e8

而在业务层里,服务与服务之间使用feign来实现调用,而授权的代码我们可以通过拦截器实现,在feign请求之前,把当前服务的token添加到目标服务的请求头就可以了

一般是这样实现的

/**
 * 发送FeignClient设置Header信息.
 * http://www.itmuch.com/spring-cloud-sum/hystrix-threadlocal/
 * Hystrix传播ThreadLocal对象
 */
@Component
public class TokenFeignClientInterceptor implements RequestInterceptor {
  /**
   * token放在请求头.
   *
   * @param requestTemplate 请求参数
   */
  @Override
  public void apply(RequestTemplate requestTemplate) {
    RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
    if (requestAttributes != null) {
      HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
      requestTemplate.header("Authorization", request.getHeader("Authorization"));
    }
  }
}

上面的拦截器代码没有什么问题,也很好理解,但事实上,当你的feign开启了hystrix功能,如果开启了,需要把hystrix的策略进行修改,默认是THREAD的,这个级别时ThreadLocal是空的,所以你的授权不能传给feign的拦截器.

hystrix: 
    command: 
        default: 
            execution: 
                isolation: 
                    strategy: SEMAPHORE 

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • spring cloud Feign使用@RequestLine遇到的坑

    Feign使用@RequestLine遇到的坑 如何在微服务项目中调用其它项目的接口试使用spring cloud feign声明式调用. /** * 客户端请去 * @author RAY * */ @FeignClient(name="store",configuration=FooConfiguration .class) public interface UserFeignClient { @RequestLine("GET /simple/{id}") p

  • 详解spring cloud feign踩坑记录

    1:多客户端时,feign接口抽取到公共jar中,此时,客户端的启动类上需要对该jar中feign所在的包进行扫描,要在spring和feign中同时注册,否则启动时会报:"Consider defining a bean of type '******Feign' in your configuration." @SpringBootApplication @EnableTransactionManagement @EnableDiscoveryClient @ComponentSc

  • Spring Cloud中关于Feign的常见问题总结

    一.FeignClient接口,不能使用@GettingMapping 之类的组合注解 代码示例: @FeignClient("microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET) public User findById(@PathVariable(&quo

  • spring cloud oauth2 feign 遇到的坑及解决

    目录 springcloudoauth2feign遇到的坑 客户端模式 基于springsecurity springcloud微服务增加oauth2权限后feign调用报null 一般是这样实现的 spring cloud oauth2 feign 遇到的坑 关于oauth2相关的内容这里不重复描述,在spring cloud中在管理内部api时鉴权相信有很多人会有疑问,这里描述两种比较low的用法,由于公司内部使用的是阿里云edas这里仅仅是记录一下,如果有更好的用法在请赐教,不喜勿喷! 客

  • spring cloud 之 Feign 使用HTTP请求远程服务的实现方法

    一.Feign 简介 在spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端.我们可以使用JDK原生的URLConnection.Apache的Http Client.Netty的异步HTTP Client, Spring的RestTemplate.但是,用起来最方便.最优雅的还是要属Feign了. Feign是一种声明式.模板化的HTTP客户端.在Spring Cloud中使用Feign, 我们可以做到使用

  • Spring Cloud使用Feign实现Form表单提交的示例

    之前,笔者写了<使用Spring Cloud Feign上传文件>.近日,有同事在对接遗留的Struts古董系统,需要使用Feign实现Form表单提交.其实步骤大同小异,本文附上步骤,算是对之前那篇的补充. 添加依赖: <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>

  • spring cloud oauth2 实现用户认证登录的示例代码

    需求 在微服务架构中,我们有很多业务模块,每个模块都需要有用户认证,权限校验.有时候也会接入来自第三方厂商的应用.要求是只登录一次,即可在各个服务的授权范围内进行操作.看到这个需求,立马就想到了这不就是单点登录吗?于是基于这样的需求,作者使用spring-cloud-oauth2去简单的实现了下用户认证和单点登录. 相关介绍 OAuth2 OAuth2是一个关于授权的网络标准,他定制了设计思路和执行流程.OAuth2一共有四种授权模式:授权码模式(authorization code).简化模式

  • Spring Cloud OAuth2中/oauth/token的返回内容格式

    目录 背景 实现原理 代码实现 相关类 关键切面拦截器 背景 在前后端分离的项目中,一般后端返回给前端的格式是一个固定的json格式.在这个前提下,Spring Cloud OAuth2 生成access token的请求/oauth/token的返回内容就需要自定义. 访问/oauth/token示例如下: 原始返回值的格式如下: 我们希望使用我们自己固定的json格式,如下: 实现原理 原理就是通过切面编程实现对/oauth/token端点请求的结果进行拦截封装处理,由于/oauth/tok

  • Spring Cloud OAuth2实现自定义token返回格式

    目录 问题描述 解决方案 总结 最近读者朋友针对Spring Security OAuth2.0 想要陈某补充一些知识,如下: 今天这篇文章就来回答其中一个问题:如何自定义token的返回格式? 问题描述 Spring Security OAuth的token返回格式都是默认的,但是往往这个格式是不适配系统,/oauth/token返回的格式如下: { "access_token": token "token_type": "bearer", &

  • Spring Cloud oauth2 认证服务搭建过程示例

    目录 安装httpie 导入数据库脚本 sts中导入项目 修改 POM文件 修改配置文件 修改主类文件 编译,运行 测试 查看Redis缓存 安装httpie 安装httpie 需要 python 环境 pip install --upgrade httpie 进入D:\Project目录,在此目录下打开CMD,调用httpie,创建 oauth2 项目 http -d https://start.spring.io/starter.zip javaVersion==17 groupId==co

  • 解决Spring Cloud中Feign/Ribbon第一次请求失败的方法

    前言 在Spring Cloud中,Feign和Ribbon在整合了Hystrix后,可能会出现首次调用失败的问题,要如何解决该问题呢? 造成该问题的原因 Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码.而首次请求往往会比较慢(因为Spring的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了.知道原因后,我们来总结一下解决放你. 解决方案有三种,以feign为例. 方法一 hystrix.command.default.execution.

  • Spring Cloud OAuth2 实现用户认证及单点登录的示例代码

    OAuth 2 有四种授权模式,分别是授权码模式(authorization code).简化模式(implicit).密码模式(resource owner password credentials).客户端模式(client credentials),具体 OAuth2 是什么,可以参考这篇文章.(http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html) 本文我们将使用授权码模式和密码模式两种方式来实现用户认证和授权管理. OAuth2 其

  • Spring cloud oauth2如何搭建认证资源中心

    一 认证中心搭建 添加依赖,如果使用spring cloud的话,不管哪个服务都只需要这一个封装好的依赖即可 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> 配置spring security /** * security配置类 */

随机推荐