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.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; } |
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.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); } } } } |
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.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; } } |
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.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依赖
1
2
3
4
5
|
< dependency > < groupId >com.liuzm.paascloud.common</ 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.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); } |
认证服务器配置
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.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配置
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://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的拦截器.
1
2
3
4
5
6
|
hystrix: command: default: execution: isolation: strategy: SEMAPHORE |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/liu_zhaoming/article/details/78840056