springboot-HTTP通信加解密

实现RequestBodyAdvice接口实现解密

允许在请求消息体在被读取及调用convert转换成实体之前做一些个人化操作,
作用于含有@RequestBody注解的请求。
实现此接口的类,需要在RequestMappingHandlerAdapter中配置或通过@ControllerAdvice注解配置。

实现ResponseBodyAdvice接口实现加密

在消息转换前处理返回值
我们实现这个接口的代码主要在这个方法里被调用
RequestResponseBodyAdviceChain.processBody,
先执行ResponseBodyAdvice.supports看当前切面类是否支持,
如果支持再调用ResponseBodyAdvice.beforeBodyWrite方法并返回
返回值会被HttpMessageConverter.write接口在进行最终的转换(例如转JSON)
然后写回前端

在这里插入图片描述

链接: SpringMVC进阶 - 利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值.

链接: SpringMvc/SpringBoot HTTP通信加解密.

链接: SpringBoot 请求消息体解密(通信加密解密).

链接: 加解密测试网站.

链接: 对称加密-SymmetricCrypto.

    <dependencies>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.49</version>
        </dependency>
    </dependencies>
import java.io.Serializable;

public class ApiResult<T> implements Serializable {

    private T data;

    private int code;

    private String msg;

    /**
     * 请求成功回调
     */
    public static ApiResult successMsg() {
        return new ApiResult().setCode(200).setMsg("ok");
    }

    /**
     * 请求成功回调
     * @param Object 对象参数
     */
    public static ApiResult successMsg(Object Object) {
        return new ApiResult().setCode(200).setMsg("ok").setData(Object);
    }

    /**
     * 请求失败回调
     * @param code 状态码
     * @param msg 描述信息
     */
    public static ApiResult errorMsg(int code, String msg) {
        return new ApiResult().setCode(code).setMsg(msg);
    }

    /**
     * 请求失败回调
     *  @param msg 描述信息
     */
    public static ApiResult errorMsg(String msg) {
        return new ApiResult().setCode(500).setMsg(msg);
    }

    public T getData() {
        return data;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public ApiResult<T> setData(T data) {
        this.data = data;
        return this;
    }

    public ApiResult<T> setCode(Integer code) {
        this.code = code;
        return this;
    }

    public ApiResult<T> setMsg(String msg) {
        this.msg = msg;
        return this;
    }

}

import lombok.Data;

@Data
public class TestReq {

    private String name;


}

TestController

import com.zm.config.SecretBody;
import com.zm.config.SecretResponseBody;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

//@SecretBody // 作用全部类
@RestController
public class TestController {

    @SecretResponseBody
    @SecretBody // 作用当前类
    @PostMapping("test")
    public ApiResult test1(@RequestBody TestReq testReq){
        System.out.println(testReq);
        return ApiResult.successMsg(testReq);
    }

    @GetMapping("test")
    public ApiResult test2(@RequestBody TestReq testReq){
        System.out.println(testReq);
        return ApiResult.successMsg(testReq);
    }

}

请求体解密注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface SecretBody {

}

响应体加密注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface SecretResponseBody {

}

Des工具类

import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.DES;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class DesCbcUtil {

    private final String DES_SECRET_KEY = "b2c17b46e2b1415392aab5a82869856c";

    private final String DES_IV = "61960842";

    private DES des = new DES(Mode.CBC, Padding.PKCS5Padding, DES_SECRET_KEY.getBytes(), DES_IV.getBytes());

    /**
     * 3DES加密
     *
     * @param plainText 普通文本
     * @return 加密后的文本,失败返回null
     */
    public String encode(String plainText) {
        String result = null;
        try {
            result = des.encryptBase64(plainText);
        } catch (Exception e) {
            log.error("DesCbcUtil encode error : {}", e);
        }
        return result;
    }

    /**
     * 3DES解密
     *
     * @param encryptText 加密文本
     * @return 解密后明文,失败返回null
     */
    public String decode(String encryptText) {
        String result = null;
        try {
            result = des.decryptStr(encryptText);
        } catch (Exception e) {
            log.error("DesCbcUtil decode error : {}", e.getMessage());
        }
        return result;
    }


}

SecretHttpMessage

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;

import java.io.InputStream;

@AllArgsConstructor
@NoArgsConstructor
public class SecretHttpMessage implements HttpInputMessage {

    private InputStream body;

    private HttpHeaders httpHeaders;

    @Override
    public InputStream getBody() {
        return this.body;
    }

    @Override
    public HttpHeaders getHeaders() {
        return this.httpHeaders;
    }
}

接收前端请求体解密

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;

@Slf4j
@ControllerAdvice
@Order(1)
public class SecretRequestAdvice implements RequestBodyAdvice {

    @Autowired
    private DesCbcUtil DesCbcUtil;

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        //如果支持加密消息,进行消息解密。
        boolean supportSafeMessage = supportSecretRequest(parameter);
        String httpBody;
        if (supportSafeMessage) {
            // 拓展:可以根据注解设置的参数,进行不同解密方式的选择
            httpBody = decryptBody(inputMessage);
            if (httpBody == null) {
                throw new HttpMessageNotReadableException("request body decrypt error", inputMessage);
            }
        } else {
            httpBody = StreamUtils.copyToString(inputMessage.getBody(), Charset.defaultCharset());
        }
        //返回处理后的消息体给messageConvert
        return new SecretHttpMessage(new ByteArrayInputStream(httpBody.getBytes()), inputMessage.getHeaders());
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    /**
     * 是否支持加密消息体
     *
     * @param methodParameter methodParameter
     * @return true/false
     */
    private boolean supportSecretRequest(MethodParameter methodParameter) {
        //判断class是否存在注解
        if (methodParameter.getContainingClass().getAnnotation(SecretBody.class) != null) {
            return true;
        }
        //判断方法是否存在注解
        return methodParameter.getMethodAnnotation(SecretBody.class) != null;
    }

    /**
     * 解密消息体,3des解析(cbc模式)
     *
     * @param inputMessage 消息体
     * @return 明文
     */
    private String decryptBody(HttpInputMessage inputMessage) throws IOException {
        InputStream encryptStream = inputMessage.getBody();
        String encryptBody = StreamUtils.copyToString(encryptStream, Charset.defaultCharset());
        return DesCbcUtil.decode(encryptBody);
    }

}

返回前端响应体加密

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@Slf4j
@Component
@ControllerAdvice(basePackages = "com.zm.controller")
public class SecretResponseAdvice implements ResponseBodyAdvice {

    @Autowired
    DesCbcUtil desCbcUtil;

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return returnType.getMethodAnnotation(SecretResponseBody.class) != null;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (returnType.getMethodAnnotation(SecretResponseBody.class) != null) {
            // 拓展:可以根据注解设置的参数,进行不同加密方式的选择
            String encode = desCbcUtil.encode(JSON.toJSONString(body));
            return encode;
        }
        return body;

    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值