好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

spring cloud oauth2 feign 遇到的坑及解决

spring cloud oauth2 feign 遇到的坑

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

客户端模式

提供三方jar包

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

< 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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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测试数据

  */

@Data

@ConfigurationProperties (prefix = "paascloud.oauth2.client" )

public class Oauth2ClientProperties {

    private String id;

    private String accessTokenUrl;

    private String clientId;

    private String clientSecret;

    private String clientAuthenticationScheme;

}

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

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测试数据

  */

@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测试数据

      */

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

            }

        }

    }

}

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

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测试数据mon.AuthenticationScheme;

/**

  * The class O auth 2 feign auto configuration.

  *

  * @author paascloud.net @gmail测试数据

  */

@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;

    }

}

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

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测试数据

  */

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依赖

?

1

2

3

4

5

< dependency >

            < groupId >com.liuzm.paascloud测试数据mon</ groupId >

            < artifactId >paascloud-security-feign</ artifactId >

            < version >1.0-SNAPSHOT</ version >

</ dependency >

@FeignClient加入configuration属性

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

/**

  * The interface Mdc product feign api.

  * @author paascloud.net@gmail测试数据

  */

@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);

}

认证服务器配置

?

1

2

3

4

@Override

    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients.withClientDetails(restClientDetailsService);

    }

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

package com.paascloud.provider.security;

import com.paascloud.security.core.properties.OAuth2ClientProperties;

import com.paascloud.security.core.properties.SecurityProperties;

import org.apache测试数据mons.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测试数据

  */

@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配置

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

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拦截器里处理的逻辑改一下,代码如下

?

1

2

3

4

5

6

7

8

@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才可以正常访问被授权的页面。

?

1

2

Content-Type:application/json

Authorization:Bearer d79c064c- 8675 - 4047 -a119-fac692e447e8

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

一般是这样实现的

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

/**

  * 发送FeignClient设置Header信息.

  * http://HdhCmsTestitmuch测试数据/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的拦截器.

?

1

2

3

4

5

6

hystrix: 

     command: 

         default: 

             execution: 

                 isolation: 

                     strategy: SEMAPHORE 

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

原文链接:https://blog.csdn.net/liu_zhaoming/article/details/78840056

查看更多关于spring cloud oauth2 feign 遇到的坑及解决的详细内容...

  阅读:20次