feign请求签名统一处理

背景

在实际项目中经常有需要对接第三方开放平台的接口,这些接口通畅需要token、签名等,本文使用feign的扩展解决这一类型问题

聚水潭出库单demo

接口

/**
 * 聚水潭出库单查询
 * @author authorZhao
 * @since 2022-11-17
 */
@FeignClient(value = "JuShuiTanOdersOutFeign",
        url = "${com.git.client.jst.url:https://openapi.jushuitan.com}/open",
        //url = "http://127.0.0.1:9199/api/im/test",
        configuration = MyConfiguration.class,
        contextId = "JuShuiTanOdersOutFeign")
public interface JuShuiTanOdersOutFeign {
    
    

    /**
     * <a href="https://openweb.jushuitan.com/dev-doc?docType=8&docId=34">聚水潭出库单查询</a>
     * 参数为什么写object和map目的就是为了写出不可维护的代码
     * @param object
     * @return
     */
    @PostMapping(value = "/orders/out/simple/query",consumes = "application/x-www-form-urlencoded;charset=UTF-8")
    AjaxResult<JstOrderOutDTO> query(Object object);

    @PostMapping(value = "/other/inout/query",consumes = "application/x-www-form-urlencoded;charset=UTF-8")
    AjaxResult<Map> otherInoutQuery(Map map);

}

自定义配置

@Slf4j
public class MyConfiguration {
    
    
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private JstConfig jstConfig;

    @Bean
    @Scope("prototype")
    public Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
    
    
        return new MyFormEncoder(new SpringEncoder(messageConverters),jstConfig,redisTemplate);
    }

    public static class MyFormEncoder extends FormEncoder{
    
    
        private final JstConfig jstConfig;
        private final RedisTemplate redisTemplate;
        public MyFormEncoder(Encoder delegate, JstConfig jstConfig,RedisTemplate redisTemplate) {
    
    
            super(delegate);
            this.jstConfig = jstConfig;
            this.redisTemplate = redisTemplate;
        }

        @Override
        public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
    
    
            Object o = redisTemplate.opsForValue().get(RedisPreEnum.JST_TOKEN.getType());
            String accessToken = Optional.ofNullable(o).map(Object::toString).map(i->JSON.parseObject(i, JstTokenDTO.class))
                    .map(JstTokenDTO::getAccess_token).orElse(StringUtils.EMPTY);
            String jsonString = Optional.ofNullable(JSON.toJSONString(object)).orElse("{}");
            Map<String,String> map = new HashMap<>();
            map.put("timestamp",String.valueOf((int)(System.currentTimeMillis()/1000)));
            map.put("version","2");
            map.put("charset","utf-8");
            map.put("access_token",accessToken);
            map.put("app_key",jstConfig.getAppKey());
            map.put("biz",jsonString);
            SignUtil.sign(jstConfig.getAppSecret(), map);
            log.info("jst param={}",JSON.toJSONString(map));
            super.encode(map,Encoder.MAP_STRING_WILDCARD,template);
        }
    }
}

解析

上述注意事项

1.聚水潭的请求接口content-type不是常见的json而是form表单,这需要注意等下结合这个需要做自定义处理

使用@RequestBody最终会被解析为json类型的

如果在feign里面使用Map<String,?>这种类型的参数在encode时候会被当做表单处理,本文这可能是Object也可能是Map<?,?>所以需要自定义解码

2.为什么token或者签名不在拦截器里面处理

拦截器在encode之后,到那时在处理数据会被处理多次没有必要

3.自定义解码集成springEncode最终将类型固定为Encoder.MAP_STRING_WILDCARD 这样子会以表单形式提交

U9案例

配置

/**
 * @author authorZhao
 * @summary fegin 客户端的自定义配置
 */
@Slf4j
public class U9FeignJsonConfiguration {
    
    
    @Autowired
    private ThirdBasicComponent thirdBasicComponent;


    @Bean
    public RequestInterceptor headerToken() {
    
    
        return template -> {
    
    
            String token = thirdBasicComponent.u9Token();
            if(StringUtils.isNotBlank(token)){
    
    
                template.header("token",token);
            }else {
    
    
                log.warn("u9 token获取失败");
            }
        };
    }

    @Bean
    @Scope("prototype")
    public Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
    
    
        return new U9FeignJsonConfiguration.MyFormEncoder(new SpringEncoder(messageConverters));
    }

    public static class MyFormEncoder extends FormEncoder {
    
    
        public MyFormEncoder(Encoder delegate) {
    
    
            super(delegate);
        }

        @Override
        public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
    
    
            template.body(JSON.toJSONString(object));
            //super.encode(object,bodyType,template);
//            String typeName = bodyType.getTypeName();
//
        }
    }


}

u9的接口就直接忽略了,直接看配置,解释问题

注意事项:

1.U9只需要token,这时候在拦截器里面处理最方便

2.为什么U9的编码也需要自定义处理

u9的请求参数是json形式,但是U9的系统是c#编写的,里面解析json如果有的值为null会解析报错(把这个key去除没问题),我们改不了别人的接口,只能改自己的json序列化方式,为例避免全局影响,直接使用fastjson2处理

其他事项

1.每次请求获取token使用redis这个可以优化增加多级缓存

2.如何刷新token

  • 聚水潭新旧token有五分钟共存时间,基本问题不大定时刷新
  • u9的每次请求判断token是否国过期,快过期的时候刷新,这时候如果并发量很大会有问题,本文主要是数据同步,数据量没那么大随便操作

本文原创,转载请申明

猜你喜欢

转载自blog.csdn.net/weixin_43328357/article/details/132608645