@RetrofitClient配合自定义拦截注解@Sign完成加签加密

快速开始

引入依赖

<dependency>
    <groupId>com.github.lianjiatech</groupId>
   <artifactId>retrofit-spring-boot-starter</artifactId>
   <version>3.0.3</version>
</dependency>

如果启动失败,大概率是依赖冲突,烦请引入或者排除相关依赖

定义HTTP接口

接口必须使用@RetrofitClient注解标记!HTTP相关注解可参考官方文档:retrofit官方文档

@RetrofitClient(baseUrl = "${test.baseUrl}")
public interface UserService {

   /**
    * 根据id查询用户姓名
    */
   @POST("getName")
   String getName(@Query("id") Long id);
}

自定义拦截注解

有的时候,我们需要在"拦截注解"动态传入一些参数,然后在拦截的时候使用这些参数。 这时候,我们可以使用"自定义拦截注解",步骤如下:

  1. 自定义注解。必须使用@InterceptMark标记,并且注解中必须包括include、exclude、handler字段。
  2. 继承BasePathMatchInterceptor编写拦截处理器
  3. 接口上使用自定义注解

例如,我们需要"在请求头里面动态加入accessKeyIdaccessKeySecret签名信息才能再发起HTTP请求",这时候可以自定义@Sign注解来实现。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@InterceptMark
public @interface Sign {
    
    String accessKeyId();

    String accessKeySecret();

    String[] include() default {"/**"};

    String[] exclude() default {};

    Class<? extends BasePathMatchInterceptor> handler() default SignInterceptor.class;
}

@Sign注解中指定了使用的拦截器是SignInterceptor

实现SignInterceptor
@Component
@Setter
public class SignInterceptor extends BasePathMatchInterceptor {

   private String accessKeyId;

   private String accessKeySecret;

   @Override
   public Response doIntercept(Chain chain) throws IOException {
      Request request = chain.request();
      Request newReq = request.newBuilder()
              .addHeader("accessKeyId", accessKeyId)
              .addHeader("accessKeySecret", accessKeySecret)
              .build();
      Response response = chain.proceed(newReq);
      return response.newBuilder().addHeader("accessKeyId", accessKeyId)
              .addHeader("accessKeySecret", accessKeySecret).build();
   }
}

注意:accessKeyIdaccessKeySecret字段必须提供setter方法。

拦截器的accessKeyIdaccessKeySecret字段值会依据@Sign注解的accessKeyId()accessKeySecret()值自动注入,如果@Sign指定的是占位符形式的字符串,则会取配置属性值进行注入。

接口上使用@Sign
@RetrofitClient(baseUrl = "${test.baseUrl}")
@Sign(accessKeyId = "${test.accessKeyId}", accessKeySecret = "${test.accessKeySecret}", include = "/api/user/getAll")
public interface InterceptorUserService {

   /**
    * 查询所有用户信息
    */
   @GET("getAll")
   Response<List<User>> getAll();

}

Request进行加签加密

获取Request的请求参数
   private String getRequestParam(Request request) throws IOException{
      String requestParam = "";
      switch (request.method().toLowerCase()) {
         case "post":
            requestParam = getRequestParams4Post(request);//请求参数
            break;
         case "get":
            requestParam = getRequestParams4Get(request.url().toString());
            break;
      }
      return requestParam;
   }

   private String getRequestParams4Get(String url) {
      HttpUrl uri = HttpUrl.parse(url);
      JSONObject requestJson = new JSONObject();

      for (String paramKey : uri.queryParameterNames()) {
         List<String> paramValue = uri.queryParameterValues(paramKey);
         requestJson.put(paramKey, paramValue);
      }
      return requestJson.toString();
   }

   /**
    * 获取POST请求参数
    */
   private String getRequestParams4Post(Request request) throws IOException {
      if (request.body() != null) {
         return parseRequestParams4Post(request);
      }
      // 说明:multipart/form-data 需要特殊处理
      return "";
   }

   /**
    * 解析请求服务器的请求参数
    */
   private static String parseRequestParams4Post(Request request) throws UnsupportedEncodingException {
      try {
         RequestBody body = request.body();
         if (body == null) {
            return "";
         }
         Buffer requestBuffer = new Buffer();
         body.writeTo(requestBuffer);
         Charset charset = StandardCharsets.UTF_8;
         MediaType contentType = body.contentType();

         if (contentType != null) {
            charset = contentType.charset(charset);
         }
         String text = requestBuffer.readString(charset);

         if (contentType != null && !"json".equals(contentType.subtype())) {
            text = URLDecoder.decode(text, convertCharset(charset));
         }

         return text;
      } catch (IOException e) {
         e.printStackTrace();
         return "{\"error\": \"" + e.getMessage() + "\"}";
      }
   }

   private static String convertCharset(Charset charset) {
      String s = charset.toString();
      int i = s.indexOf("[");
      if (i == -1){
         return s;
      }
      return s.substring(i + 1, s.length() - 1);
   }

目前还不支持multipart/form-data类型的参数

对参数进行加签加密
   private Request encrypt(Request request) throws IOException {
      String aesKey = AESUtil.generateAESKey();
      String key = RSAUtil.encryptByPublicKey(aesKey, rsaPublicKey);
      String requestParam = getRequestParam(request);
      String data = AESUtil.encrypt(requestParam, aesKey);
      String timestamp = String.valueOf(System.currentTimeMillis());
      String nonce = UUID.randomUUID().toString().replaceAll("-", "");
      String text = SignUtil.getContent(requestParam, nonce, timestamp);
      String sign = RSAUtil.signByPrivateKey(text, rsaPrivateKey);
      ApiSecurityParam apiSecurityParam = new ApiSecurityParam();
      apiSecurityParam.setAppId(accessKeyId);
      apiSecurityParam.setData(data);
      apiSecurityParam.setKey(key);
      apiSecurityParam.setNonce(nonce);
      apiSecurityParam.setSign(sign);
      apiSecurityParam.setTimestamp(timestamp);
      apiSecurityParam.toString().getBytes(StandardCharsets.UTF_8);
      RequestBody requestBody = RequestBody.create(request.body().contentType(), JSON.toJSONString(apiSecurityParam));
      Request newReq = request.newBuilder()
              .method(request.method(),requestBody)
              .build();
      return newReq;
   }

这里具体加签加密方式大家可以自行替换。

参考文献:retrofit-spring-boot-starter: A spring-boot starter for retrofit, supports rapid integration and feature enhancements.(适用于retrofit的spring-boot启动器,支持快速集成和功能增强)icon-default.png?t=N7T8https://gitee.com/lianjiatech/retrofit-spring-boot-starter

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值