A-接口加密&解密

Java - 接口加密&解密

需求 & 解决思路

需求:Spring Boot 项目中,对部分POST请求的参数 - body进行解密处理。对部分接口返回的值进行加密处理。

解决思路:

  • 首先第一反应就是使用AOP,加密&解密 是边缘业务且是可复用的。
  • 其次,加密 & 解密 操作都是针对接口而言的,那么SpringBoot内有提供了对接口的拦截类

代码实现

解密操作

首先,创建一个注解类:Decrypt,用来标识哪些接口的参数需要解密。

@Documented
@Retention(RetentionPolicy.RUNTIME)//元注解,标识当前注解需要保留至JVM中
@Target({ElementType.METHOD, ElementType.PARAMETER})//表示该注解作用在方法和参数上
public @interface Decrypt {
}

创建DecryptRequest类,真正实现解密操作。

@ControllerAdvice
public class DecryptRequest extends RequestBodyAdviceAdapter {

    //这里使用的Hutool工具包内提供的RSA加密算法,RSA_PRIVATE_KEY 这个参数需要自己生成
    private static final RSA rsa_de = new RSA(RSA_PRIVATE_KEY, null);

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        //首先此处判断该接口上是否存在@Decrypt注解,不存在则不需要处理
        return methodParameter.hasMethodAnnotation(Decrypt.class) || methodParameter.hasParameterAnnotation(Decrypt.class);
    }

    @Override
    public HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        byte[] body = new byte[inputMessage.getBody().available()];
        inputMessage.getBody().read(body);
        try {
            String s = new String(body);
            byte[] decrypt = rsa_de.decrypt(s, KeyType.PrivateKey);
            return new HttpInputMessage() {
                @Override
                public InputStream getBody(){
                    return new ByteArrayInputStream(decrypt);
                }

                @Override
                public HttpHeaders getHeaders() {
                    return inputMessage.getHeaders();
                }
            };
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
    }
}
  • @ControllerAdvice:该注解主要是针对控制器进行拦截操作 - 即就是AOP。具体的拦截是需要在注解类内部的方法进行实现。通常有以下用法:

    • @ExceptionHandler:用来定义全局异常处理类
    • @ModelAttribute:用来预设全局的数据
    • @InitBinder:实现对请求参数的预处理
  • RequestBodyAdvice:它是Spring提供的一个全局解决方案,用来解决对请求参数做的一些统一的操作,如:参数过滤、字符编码、加解密等。它仅对使用了 @RequestBody 注解的控制器生效生效。它是一个顶级接口,具体功能由子类实现。

    • RequestBodyAdviceAdapter:RequestBodyAdvice 的抽象实现类,通常我们只需要集成这个类按需实现。

      public abstract class RequestBodyAdviceAdapter implements RequestBodyAdvice {
      
      	/**
      	 * 默认实现返回传入的输入消息
      	 */
      	@Override
      	public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
      			Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
      
      		return inputMessage;
      	}
      
      	/**
      	 * 默认实现返回传入的正文
      	 */
      	@Override
      	public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
      			Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
      
      		return body;
      	}
      
      	/**
      	 * 默认实现返回传入的正文。
      	 */
      	@Override
      	@Nullable
      	public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
      			Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
      
      		return body;
      	}
      
      }
      

    具体使用

    @RestController
    @RequestMapping("/api/test")
    public class TestController {
    
        @PostMapping("decrypt")
        public String decrypt(@RequestBody @Decrypt UserVo vo) {
            return vo.toString();
        }
    }
    

接口返回值加密

首先项目中需要统一返回值格式

@Data //这里使用了lombok 插件
@Accessors(chain = true)
@AllArgsConstructor
public class ResultData implements Serializable {
    private static final long serialVersionUID = 8545996863226528798L;
    private Integer code = 200;
    private String msg = "success";
    private Object data;

    private ResultData(Object data) {
        this.data = data;
    }

    public static ResultData ok() {
        return new ResultData(null);
    }

    public static ResultData ok(Object data) {
        return new ResultData(data);
    }

    public static ResultData error(ErrorResultEnum resultEnum) {
        return new ResultData(resultEnum.getCode(), resultEnum.getMsg(), null);
    }

    public static ResultData error(Integer code, String msg) {
        return new ResultData(code, msg, null);
    }
}

之后,返回参数的加密是针对ResultData中的data属性而言的。

第一步:创建一个注解:@Encrypt

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Encrypt {
}
@ControllerAdvice
public class EncryptResponse implements ResponseBodyAdvice<Object> {

    private static final RSA rsa_de = new RSA(RSA_PRIVATE_KEY, null);

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return returnType.hasMethodAnnotation(Encrypt.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        ResultData resultData = (ResultData) body;
        Object data = resultData.getData();
        ObjectMapper ob = new ObjectMapper();
        if (data!= null){
            String s = null;
            try {
                s = rsa_de.encryptBase64(ob.writeValueAsString(data), KeyType.PrivateKey);
                resultData.setData(s);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return resultData;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值