使用SpringBoot自定义starter完成接口的加密解密

声明: 这里加密解密只是简单使用了一个base64操作的,主要是提供功能案例,具体的根据需要改

maven依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.15</version>
        </dependency>
    </dependencies>

请求参数解密类

package luck.spring.boot.starter.encrypt.demo.advice;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import luck.spring.boot.starter.encrypt.demo.anno.Decrypt;
import luck.spring.boot.starter.encrypt.demo.config.LuckEncryptProperties;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonInputMessage;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.Type;

@ControllerAdvice
@RequiredArgsConstructor
public class LuckDecryptBodyAdvice implements RequestBodyAdvice {

    private final LuckEncryptProperties luckEncryptProperties;

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return methodParameter.hasMethodAnnotation(Decrypt.class) && luckEncryptProperties.isDecryptEnable() && methodParameter.hasParameterAnnotation(RequestBody.class);
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        byte[] bytes = IoUtil.readBytes(inputMessage.getBody());
        byte[] decrypt = Base64Utils.decode(bytes);
        return new MappingJacksonInputMessage(new ByteArrayInputStream(decrypt), 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 StrUtil.EMPTY_JSON;
    }
}

请求响应加密类

package luck.spring.boot.starter.encrypt.demo.advice;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import luck.spring.boot.starter.encrypt.demo.anno.Encrypt;
import luck.spring.boot.starter.encrypt.demo.config.LuckEncryptProperties;
import luck.spring.boot.starter.encrypt.demo.inter.LuckResultHandler;
import luck.spring.boot.starter.encrypt.demo.util.LuckSpringUtil;
import luck.spring.boot.starter.encrypt.demo.util.Result;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Controller;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.nio.charset.StandardCharsets;
import java.util.List;

@ControllerAdvice
@RequiredArgsConstructor
public class LuckEncryptBodyAdvice implements ResponseBodyAdvice<Object> {
    private final LuckEncryptProperties luckEncryptProperties;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        Class<?> declaringClass = returnType.getDeclaringClass();
        return declaringClass.isAnnotationPresent(RestController.class)
                || (declaringClass.isAnnotationPresent(Controller.class) && returnType.getMethod().isAnnotationPresent(ResponseBody.class));

    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        Object result = body;
        List<LuckResultHandler> beans = LuckSpringUtil.getBeans(LuckResultHandler.class);
        if (CollUtil.isNotEmpty(beans)) {
            for (LuckResultHandler handler : beans) {
                if (!handler.support(body.getClass())) {
                    continue;
                }
                result = handler.handle(body);
            }
        }
        if (luckEncryptProperties.isStandardResultEnable()) {
            if (!(result instanceof Result)) {
                result = Result.OK(result);
            }
            if (returnType.hasMethodAnnotation(Encrypt.class) && luckEncryptProperties.isEncryptEnable()) {
                Result<Object> res = (Result<Object>) result;
                Object data = res.getData();
                if (ObjectUtil.isNotEmpty(data)) {
                    if (!(data instanceof String) || !JSONUtil.isTypeJSON(data.toString())) {
                        data = JSONUtil.toJsonStr(data);
                    }
                    String encrypt = Base64Utils.encodeToString(data.toString().getBytes(StandardCharsets.UTF_8));
                    res.setData(encrypt);
                }
            }
        }
        return result;
    }
}

加密解密注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Decrypt {
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypt {
}

自动配置属性类

@ConfigurationProperties(prefix = "luck.encrypt")
@Data
public class LuckEncryptProperties {
    private boolean encryptEnable;
    private boolean decryptEnable;
    private boolean standardResultEnable;
}

处理响应结果使用的消息转换器

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0, new MappingJackson2HttpMessageConverter());
    }
}

starter提供给外部的响应结果处理类接口

package luck.spring.boot.starter.encrypt.demo.inter;

public interface LuckResultHandler<P, T, R> {
    R handle(P p);

    default boolean support(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        Class<T> type = getType();
        if (type == null) {
            return false;
        }
        return clazz.equals(type);
    }

    Class<T> getType();
}

Spring容器操作的工具类

package luck.spring.boot.starter.encrypt.demo.util;

import cn.hutool.core.collection.CollUtil;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.OrderComparator;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.util.*;

@Component
public class LuckSpringUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext;
    private static Environment environment;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        LuckSpringUtil.applicationContext = applicationContext;
        LuckSpringUtil.environment = applicationContext.getEnvironment();
    }

    public static <T> T getBean(Class<T> tClass) {
        return applicationContext.getBean(tClass);
    }

    public static <T> List<T> getBeans(Class<T> tClass) {
        Collection<T> collection = Optional.ofNullable(getBeansMap(tClass))
                .filter(CollUtil::isNotEmpty)
                .map(Map::values)
                .orElseGet(ArrayList::new);
        return CollUtil.sort(collection, OrderComparator.INSTANCE);
    }

    public static <T> Map<String, T> getBeansMap(Class<T> tClass) {
        return applicationContext.getBeansOfType(tClass);
    }


    public static String getProperty(String key) {
        return environment.getProperty(key);
    }

    public static <T> T getProperty(String key, Class<T> tClass) {
        return environment.getProperty(key, tClass);
    }
}

统一响应结果类

package luck.spring.boot.starter.encrypt.demo.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;


/**
 * @author anti
 */
@NoArgsConstructor
@Data
@Accessors(chain = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Result<T> {

    @JsonProperty(index = 0)
    private int code;

    @JsonProperty(index = 1)
    private String message;

    @JsonProperty(index = 2)
    private T data;


    public Result(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public Result(int code, String message, T data) {
        this(code, message);
        this.data = data;
    }

    public static <T> Result<T> OK() {
        return new Result<>(200, "success");
    }


    public static <T> Result<T> OK(String message) {
        return new Result<>(200, message);
    }

    public static <T> Result<T> OK(String message, T data) {
        return new Result<>(200, message, data);
    }

    public static <T> Result<T> OK(T data) {
        return OK("请求成功", data);
    }


    public static <T> Result<T> FAIL() {
        return FAIL(500, "fail");
    }

    public static <T> Result<T> FAIL(String message) {
        return FAIL(500, message);
    }

    public static <T> Result<T> FAIL(int code, String message) {
        return FAIL(code, message, null);
    }

    public static <T> Result<T> FAIL(int code, String message, T data) {
        return new Result<>(code, String.format("请求失败,原因为:%s", message), data);
    }
}

对外暴露的,写在spring.factories文件的配置类


@ComponentScan("luck.spring.boot.starter.encrypt.demo")
@EnableConfigurationProperties(LuckEncryptProperties.class)
public class LuckEncryptConfig {
}

resources文件下,META-INF目录下的spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
luck.spring.boot.starter.encrypt.demo.expose.LuckEncryptConfig

到此,starter已经完成了,需要使用maven:install安装到仓库,开始使用starter

 maven依赖

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>luck.spring.boot.starter.encrypt.demo</groupId>
            <artifactId>luck-spring-boot-starter-encrypt-demo</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

接口

@RestController
public class LuckController {
    @PostMapping("/test")
    @Encrypt
    @Decrypt
    public String test(@RequestBody Map<String, Object> map) {
        return JSONUtil.toJsonStr(map);
    }
}

自定义starter提供的扩展接口,可以处理统一结果集

这就是简单的案例,告知怎么使用,下面做的其实没啥意义,真实根据业务来处理

package luck.spring.boot.handler;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import luck.spring.boot.starter.encrypt.demo.inter.LuckResultHandler;
import luck.spring.boot.starter.encrypt.demo.util.Result;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class ResultHandler implements LuckResultHandler<Object, String, Result> {
    @Override
    public Result handle(Object result) {
        if ((result instanceof Result)) {
            return (Result) result;
        }
        if (ObjectUtil.isEmpty(result)) {
            return Result.OK(result);
        }
        if (result instanceof String && JSONUtil.isTypeJSON(result.toString())) {
            result = JSONUtil.toBean(result.toString(), Map.class);
        }
        return Result.OK(result);
    }

    @Override
    public Class<String> getType() {
        return String.class;
    }
}

启动类

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值