Spring Cloud + Nacos + Seata整合过程(分布式事务解决方案)
411
2022-08-23
spring cloud oauth2 feign 遇到的坑及解决
目录springcloudoauth2feign遇到的坑客户端模式基于springsecurityspringcloud微服务增加oauth2权限后feign调用报null一般是这样实现的
spring cloud oauth2 feign 遇到的坑
关于oauth2相关的内容这里不重复描述,在spring cloud中在管理内部api时鉴权相信有很多人会有疑问,这里描述两种比较low的用法,由于公司内部使用的是阿里云edas这里仅仅是记录一下,如果有更好的用法在请赐教,不喜勿喷!
客户端模式
提供三方jar包
这里需要抽一个jar包,需要用到feign的为服务端直接利用maven模式引入feign即可
核心类
CustomHystrixConcurrencyStrategy.javaOauth2ClientProperties.javaOAuth2FeignAutoConfiguration.javaOAuth2FeignRequestInterceptor.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
* @param callable the callable
*
* @return the callable
*/
@Override
public
return new HystrixContextWrapper
}
/**
* The class Hystrix context wrapper.
*
* @param
*
* @author paascloud.net @gmail.com
*/
public static class HystrixContextWrapper
private HystrixRequestContext hystrixRequestContext;
private Callable
/**
* Instantiates a new Hystrix context wrapper.
*
* @param delegate the delegate
*/
HystrixContextWrapper(Callable
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依赖
@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
cliehttp://nts[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://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
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~