Java请求加密 后端解密 Vue+oauth+geteway微服务 SM4国标

本来上线的微服务架构受安全检查需要 登录用户名密码加密访问 本来以为是在outh 加密原来是 从前端提交就要加密提交 后端解密授权 话不多说直接上思路和代码

思路:因为我后端用的 oauth2.0授权 所有在geteway拦截请求解密参数再转发(如果正常接口登录也是一样

因为我这边项目原因只能选择国标加密遂选择SM4 (有其他选择项的小伙伴可以自主选择

getWay拦截器重点

import cn.datax.common.exception.DataException;
import cn.datax.gateway.util.SM4Util;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.time.LocalDateTime;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.function.Consumer;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.*;

@Slf4j
@Component
@Order(0)
public class DataGatewayRequestFilter implements GlobalFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String path = request.getURI().getPath(); // 当前调用方法的url

        printLog(exchange);
   //     request.getHeaders();
        HttpHeaders header = request.getHeaders();
        log.info("header = {} 请求地址:{}", header,path);
        byte[] token = Base64Utils.encode((DataConstant.Security.TOKENVALUE.getVal()).getBytes());
        String[] headerValues = {new String(token)};
        log.info("tokenheader = {}", DataConstant.Security.TOKENHEADER.getVal());
  //      header.set(DataConstant.Security.TOKENHEADER.getVal(), String.valueOf(headerValues));
//
        String apiKey = String.valueOf(header.get("api_key")==null?"":header.get("api_key"));
        String secretKey = String.valueOf(header.get("secret_key")==null?"":header.get("secret_key"));
        if (StrUtil.isBlank(apiKey) || StrUtil.isBlank(secretKey)) {
            log.info("api_key或secret_key空");
        }else{
            //        Consumer<HttpHeaders> httpHeaders=httpHeader -> {
//            httpHeader.set("api_key",apiKey);
//            httpHeader.set("secret_key",secretKey);
//            httpHeader.set(DataConstant.Security.TOKENHEADER.getVal(), String.valueOf(headerValues));
//        };
        }

        if(path.equals("/oauth/token")){
            log.info("*********************请求拦截*********************");
            return readBody(exchange, chain);
        }


        ServerHttpRequest build = request.mutate().header(DataConstant.Security.TOKENHEADER.getVal(), headerValues).build();
//        ServerHttpRequest build = request.mutate().headers(header).build();
        ServerWebExchange newExchange = exchange.mutate().request(build).build();
        return chain.filter(newExchange);
    }

    private Mono<Void> readBody(ServerWebExchange exchange, GatewayFilterChain chain) {
        //重新构造request,参考ModifyRequestBodyGatewayFilterFactory
        ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
        //重点
        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
            //因为约定了终端传参的格式,所以只考虑json的情况,如果是表单传参,请自行增加
            if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType) || MediaType.APPLICATION_JSON_UTF8.isCompatibleWith(mediaType)|| MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)) {
                String newBody = null;
                try{
                    // 解密body
                   log.info("--------------------{}-------------------",body);// System.out.println(body);

                      String key="GJstSK_YBD=gSOFT";
                       newBody = SM4Util.decryptEcb(key,body);
                }catch (Exception e){
                    e.getMessage();
                }
                return Mono.just(newBody);
            }
            return Mono.empty();
        });
        BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(exchange.getRequest().getHeaders());
        //猜测这个就是之前报400错误的元凶,之前修改了body但是没有重新写content length
        headers.remove("Content-Length");
        //MyCachedBodyOutputMessage 这个类完全就是CachedBodyOutputMessage,只不过CachedBodyOutputMessage不是公共的
        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
        return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
            ServerHttpRequest decorator = this.decorate(exchange, headers, outputMessage);
            return returnMononew(chain, exchange.mutate().request(decorator).build());
        }));
    }
    private Mono<Void> returnMononew(GatewayFilterChain chain,ServerWebExchange exchange){
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
        }));
    }

    ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
        return new ServerHttpRequestDecorator(exchange.getRequest()) {
            public HttpHeaders getHeaders() {
                long contentLength = headers.getContentLength();
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(super.getHeaders());
                if (contentLength > 0L) {
                    httpHeaders.setContentLength(contentLength);
                } else {
                    httpHeaders.set("Transfer-Encoding", "chunked");
                }
                return httpHeaders;
            }
            public Flux<DataBuffer> getBody() {
                return outputMessage.getBody();
            }
        };
    }

    private void printLog(ServerWebExchange exchange) {
        URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
        LinkedHashSet<URI> uris = exchange.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        URI originUri = null;
        if (uris != null) {
            originUri = uris.stream().findFirst().orElse(null);
        }
        if (url != null && route != null && originUri != null) {
            log.info("转发请求:{}://{}{} --> 目标服务:{},目标地址:{}://{}{},转发时间:{}",
                    originUri.getScheme(), originUri.getAuthority(), originUri.getPath(),
                    route.getId(), url.getScheme(), url.getAuthority(), url.getPath(), LocalDateTime.now()
            );
        }
    }
}
`




**后端加解密工具类:**
```import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;


public class SM4Util {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private static final String ENCODING = "UTF-8";
    public static final String ALGORITHM_NAME = "SM4";
    // 加密算法/分组加密模式/分组填充方式
    // PKCS5Padding-以8个字节为一组进行分组加密
    // 定义分组加密模式使用:PKCS5Padding
    public static final String ALGORITHM_NAME_ECB_PADDING7 = "SM4/ECB/PKCS5Padding";
    public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding";
    // 64-16位16进制;128-32位16进制;256-64位16进制
    public static final int DEFAULT_KEY_SIZE = 128;

    /**
     * 生成ECB暗号
     *
     * @param algorithmName 算法名称
     * @param mode          模式
     * @param key
     * @return
     * @throws Exception
     * @explain ECB模式(电子密码本模式:Electronic codebook)
     */
    private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        cipher.init(mode, sm4Key);
        return cipher;
    }

    // 产生密钥

    /**
     * 自动生成密钥
     *
     * @return
     * @throws
     * @throws
     * @explain
     */
    public static byte[] generateKey() throws Exception {
        return generateKey(DEFAULT_KEY_SIZE);
    }

    /**
     * @param keySize
     * @return
     * @throws Exception
     * @explain
     */
    public static byte[] generateKey(int keySize) throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(keySize, new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    /**
     * sm4加密
     *
     * @param hexKey   16进制密钥(忽略大小写)
     * @param paramStr 待加密字符串
     * @return 返回16进制的加密字符串
     * @throws Exception
     * @explain 加密模式:ECB
     * 密文长度不固定,会随着被加密字符串长度的变化而变化
     */
    public static String encryptEcb(String hexKey, String paramStr) throws Exception {
        String cipherText = "";
        // 16进制字符串-->byte[]
        //byte[] keyData = ByteUtils.fromHexString(hexKey);
        byte[] keyData = hexKey.getBytes(ENCODING);
        // String-->byte[]
        byte[] srcData = paramStr.getBytes(ENCODING);
        // 加密后的数组
        byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData);
        // byte[]-->hexString
        //cipherText = ByteUtils.toHexString(cipherArray);
        cipherText = Base64.encodeBase64String(cipherArray);
        return cipherText;
    }

    /**
     * 加密模式之Ecb
     *
     * @param key
     * @param data
     * @return
     * @throws Exception
     * @explain
     */
    public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * sm4解密
     *
     * @param hexKey     16进制密钥
     * @param cipherText 16进制的加密字符串(忽略大小写)
     * @return 解密后的字符串
     * @throws Exception
     * @explain 解密模式:采用ECB
     */
    public static String decryptEcb(String hexKey, String cipherText) throws Exception {
        // 用于接收解密后的字符串
        String decryptStr = "";
        // hexString-->byte[]
        //byte[] keyData = ByteUtils.fromHexString(hexKey);
        byte[]  keyData = hexKey.getBytes(ENCODING);
        // hexString-->byte[]
        //byte[] cipherData = ByteUtils.fromHexString(cipherText);
        byte[] cipherData =Base64.decodeBase64(cipherText);
        // 解密
        byte[] srcData = decrypt_Ecb_Padding(keyData, cipherData);
        // byte[]-->String
        decryptStr = new String(srcData, ENCODING);
        return decryptStr;
    }

    /**
     * 解密
     *
     * @param key
     * @param cipherText
     * @return
     * @throws Exception
     * @explain
     */
    public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) throws Exception {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherText);
    }

    /**
     * 校验加密前后的字符串是否为同一数据
     *
     * @param hexKey     16进制密钥(忽略大小写)
     * @param cipherText 16进制加密后的字符串
     * @param paramStr   加密前的字符串
     * @return 是否为同一数据
     * @throws Exception
     * @explain
     */
    public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) throws Exception {
        // 用于接收校验结果
        boolean flag = false;
        // hexString-->byte[]
        //byte[] keyData = ByteUtils.fromHexString(hexKey);
        byte[] keyData = hexKey.getBytes(ENCODING);
        // 将16进制字符串转换成数组
        //byte[] cipherData = ByteUtils.fromHexString(cipherText);
        byte[] cipherData = Base64.decodeBase64(cipherText);
        // 解密
        byte[] decryptData = decrypt_Ecb_Padding(keyData, cipherData);
        // 将原字符串转换成byte[]
        byte[] srcData = paramStr.getBytes(ENCODING);
        // 判断2个数组是否一致
        flag = Arrays.equals(decryptData, srcData);
        return flag;
    }

    public static void main(String[] args) throws Exception {

        String key="GJstSK_YBD=gSOFT";
        System.out.println(decryptEcb(key,"uQ3SlpR+OrUXgYurp4Mt+cv4mkIDQu5s2JBwjw4jgFdQhiuK5Hfq0zK3/Eq/CaYKY5QJYfCb+mI87mQRYgm/Mw=="));
        System.out.println(decryptEcb(key,"cjUwtOA2mKFqTdViRH7suw=="));
        String a="username=admin&password=123456&grant_type=password&scope=all";

    }
}``````````````

**前端加解密工具类:**
posted on 2023-09-08 16:19  JavaWang  阅读(464)  评论(0)    收藏  举报