feign调用及服务间鉴权
feign 接口调用使用
以新创建的demo 模块为例,需要在 spring.factories 配置如下三个类的全路径
feign 调用包含 feign-client、 异常回调、降级逻辑实现 三个部分
// feign-client
@FeignClient(contextId = "remoteDemoService", value = "demo-biz",
fallbackFactory = RemoteDemoServiceFallbackFactory.class)
public interface RemoteDemoService {
}
// 接口降级实现 降级逻辑实现
public class RemoteDemoServiceFallbackImpl implements RemoteDemoService {
@Setter
private Throwable cause;
}
// 异常回调工厂
public class RemoteDemoServiceFallbackFactory implements FallbackFactory<RemoteDemoService> {
@Override
public RemoteDemoServiceFallbackImpl create(Throwable throwable) {
RemoteDemoServiceFallbackImpl remoteLogServiceFallback = new RemoteDemoServiceFallbackImpl();
RemoteDemoServiceFallbackImpl.setCause(throwable);
return remoteLogServiceFallback;
}
}
demo-api/src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.jk.jmake.demo.api.feign.factory.RemoteDemoServiceFallbackFactory,\
com.jk.jmake.demo.api.feign.fallback.RemoteDemoServiceFallbackImpl
① 客户端带Token 情况
如下图客户端携带token访问A服务。
A服务通过FeginClient 调用B服务获取相关依赖数据。
所以只要带token 访问A 无论后边链路有多长 ABCD 都可以获取当前用户信息
权限需要有这些整个链路接口的全部权限才能成功
② 无token请求,服务内部发起情况处理
很多情况下,比如定时任务。A服务并没有token 去请求B服务,pig也对这种情况进行了兼容。类似于A对外暴露API, 但是又安全限制
FeignClient 需要带一个请求token,FROM_IN 声明是内部调用
remoteLogService.saveLog(sysLog, SecurityConstants.FROM_IN);
@FeignClient(contextId = "remoteLogService", value = ServiceNameConstants.UPMS_SERVICE)
public interface RemoteLogService {
/**
* 保存日志
* @param sysLog 日志实体
* @param from 是否内部调用
* @return succes、false
*/
@PostMapping("/log/save")
R<Boolean> saveLog(@RequestBody SysLog sysLog, @RequestHeader(SecurityConstants.FROM) String from);
}
目标接口对内外调用进行限制添加 @Inner 注解,这样就避免接口对外暴露的安全问题。只能通过内部调用才能使用,浏览器不能直接访问该接口
@Inner
@PostMapping
public R save(@Valid @RequestBody SysLog sysLog) {
return new R<>(sysLogService.save(sysLog));
}
若代码无法加
@Inner
也可以参考API 对外暴露章节对外暴露目标接口
③ 核心代码
feign 拦截器将本服务的token 通过copyToken的形式传递给下游服务
public class JmakeFeignClientInterceptor extends OAuth2FeignRequestInterceptor {
@Override
public void apply(RequestTemplate template) {
Collection<String> fromHeader = template.headers().get(SecurityConstants.FROM);
if (CollUtil.isNotEmpty(fromHeader) && fromHeader.contains(SecurityConstants.FROM_IN)) {
return;
}
accessTokenContextRelay.copyToken();
if (oAuth2ClientContext != null
&& oAuth2ClientContext.getAccessToken() != null) {
super.apply(template);
}
}
}