客户端根据接口参数生成接口签名,在网关实现校验签名,实现防篡改

前言

实现签名算法和方式有很多,但是部分实现没有把参数放到生成签名的逻辑中,只是校验发起接口调用的身份是否合法,如果没有把参数放到生成签名当中,被抓包之后,就可以篡改参数,当然除了这种把参数放到生成签名的逻辑中,还有其他方式可以实现防篡改,例如:发起接口调用生成了2部分数据,一部分是签名,一部分是参数加密信息,生成签名逻辑中没有放入参数,客户端使用对称加密算法实现参数加密,服务端使用相同的秘钥解密,相比较把参数放到签名当中,对参数使用对称加密,也不利于自己排查问题,2种方式各有所长,自己选择,我今天是通过第一种方式,也就是把参数放到生成签名逻辑中。

代码

过滤器实现校验签名逻辑

package com.lwh.filter.signature;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import com.lwh.loancloud.common.VerifySignContact;
import com.lwh.loancloud.filter.ZuulFilterHelper;
import com.lwh.loancloud.filter.zrs.wrapper.HeaderMapRequestWrapper;
import com.lwh.loancloud.response.CommonCode;
import com.lwh.loancloud.response.ServerResponse;
import com.lwh.loancloud.utils.RequestUtil;
import com.lwh.loancloud.utils.encrypt.RSAInternalUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.net.URLDecoder;
import java.util.List;
import java.util.StringJoiner;

/**
 * @author lwh
 * @date 2024/2/27
 * @description 发起请求,校验本次请求的签名是否合规,该过滤器适配所有项目
 **/
@Component
@Slf4j
public class InternalRequestVerifySignFilter extends ZuulFilter {

    // 客户端标识,换行符分隔
    @Value("#{'${verify.sign.app.id:}'.split('\\n')}")
    private List<String> verifySignAppIdList;

    // 客户端版本号,换行符分隔
    @Value("#{'${verify.sign.version:}'.split('\\n')}")
    private List<String> verifySignVersionList;

    // 客户端公钥
    @Value("${verify.sign.uri.public.key}")
    private String verifySignUriPublicKey;

    // 接口请求需要校验签名的开关
    @Value("${verify.sign.uri.switch}")
    private Boolean verifySignUriSwitch;

    // 接口请求需要校验签名的前缀,换行符分隔
    @Value("#{'${verify.sign.uri.prefix:}'.split('\\n')}")
    private List<String> verifySignUriPrefixList;

    // 接口请求不需要校验签名的关键字,换行符分隔
    @Value("#{'${verify.sign.exclude.uri:}'.split('\\n')}")
    private List<String> verifySignExcludeUriList;

    /**
     * @author lwh
     * @date 2024/2/27
     * @description pre:请求在被路由之前执行
     * routing:在路由请求时调用
     * post:在routing和error过滤器之后调用
     * error:处理请求时发生错误调用
     **/
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * @author lwh
     * @date 2024/2/27
     * @description 过虑器序号,越小越被优先执行
     **/
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * @author lwh
     * @date 2024/2/27
     * @description true=执行该过滤器,false=不执行该过滤器
     **/
    @Override
    public boolean shouldFilter() {
        // 开关如果关闭,不再校验签名
        if (!verifySignUriSwitch) {
            return false;
        }
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        // 获取本次请求路径地址
        String requestURI = request.getRequestURI();
        // 判断在开关开启状态下,该请求是否在排除的范围内,如果存在则不再执行该过滤器
        if (CollectionUtil.isNotEmpty(verifySignExcludeUriList)) {
            boolean exclude = verifySignExcludeUriList.stream().anyMatch(k -> requestURI.contains(k));
            if (exclude) {
                return false;
            }
        }
        // 判断在开关开启状态下,本次请求是否是命中前缀,如果命中,则执行该该过滤器
        if (CollectionUtil.isNotEmpty(verifySignUriPrefixList)) {
            boolean appoint = verifySignUriPrefixList.stream().anyMatch(k -> requestURI.startsWith(k));
            if (appoint) {
                return true;
            }
        }
        return false;
    }

    /**
     * @author lwh
     * @date 2024/2/28
     * @description 校验签名
     **/
    @Override
    public Object run() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        String appId = RequestUtil.getHeadParams(request, VerifySignContact.APP_ID);
        String version = RequestUtil.getHeadParams(request, VerifySignContact.VERSION);
        String timestamp = RequestUtil.getHeadParams(request, VerifySignContact.TIMESTAMP);
        String sign = RequestUtil.getHeadParams(request, VerifySignContact.SIGN);
        StringJoiner joiner = new StringJoiner("_");
        joiner.add(appId).add(version).add(timestamp);
        try {
            // 4个参数只要有一个为空,就是非法请求
            if (StringUtils.isAnyBlank(appId, version, timestamp, sign)) {
                ZuulFilterHelper.accessDenied(ServerResponse.createByErrorCodeMessage(CommonCode.CODE_00405.code(), "hp is null"));
                return null;
            }
            if (!verifySignAppIdList.contains(appId.trim())) {
                ZuulFilterHelper.accessDenied(ServerResponse.createByErrorCodeMessage(CommonCode.CODE_00405.code(), "Illegal a"));
                return null;
            }
            if (!verifySignVersionList.contains(version.trim())) {
                ZuulFilterHelper.accessDenied(ServerResponse.createByErrorCodeMessage(CommonCode.CODE_00405.code(), "Illegal v"));
                return null;
            }
            // 文件上传放过
            String contentType = request.getHeader("Content-Type");
            if (StringUtils.isNotBlank(contentType) && contentType.contains("multipart/form-data")) {
                return null;
            }
            // 获取请求参数
            if (StringUtils.isNotBlank(request.getQueryString())) {
                joiner.add(URLDecoder.decode(request.getQueryString().trim(), "UTF-8"));
            }
            String bodyString = RequestUtil.getBodyString(request);
            if (StringUtils.isNotBlank(bodyString)) {
                joiner.add(URLDecoder.decode(bodyString.trim(), "UTF-8"));
            }
            log.warn("签名内容:{},签名:{}", joiner.toString(), sign);
            // 验证签名
            if (!RSAInternalUtil.verify(verifySignUriPublicKey, joiner.toString(), sign)) {
                ZuulFilterHelper.accessDenied(ServerResponse.createErrorByCode(CommonCode.CODE_00405));
                return null;
            }
            if (StrUtil.isNotBlank(bodyString)) {
                String decodeBody = URLDecoder.decode(bodyString.trim(), "UTF-8");
                HeaderMapRequestWrapper httpServletRequestWrapper = new HeaderMapRequestWrapper(requestContext.getRequest()) {
                    @Override
                    public ServletInputStream getInputStream() {
                        return new ServletInputStreamWrapper(decodeBody.getBytes());
                    }

                    @Override
                    public int getContentLength() {
                        return decodeBody.getBytes().length;
                    }

                    @Override
                    public long getContentLengthLong() {
                        return decodeBody.getBytes().length;
                    }
                };
                requestContext.setRequest(httpServletRequestWrapper);
            }
        } catch (Exception e) {
            ZuulFilterHelper.accessDenied(ServerResponse.createErrorByCode(CommonCode.CODE_00001));
            log.error("内部请求验签流程出现异常,签名内容:{},签名:{}\nError:{}", joiner.toString(), sign, ExceptionUtil.getMessage(e));
        }

        return null;
    }
}

签名工具类

package com.lwh.loancloud.utils.encrypt;

import org.apache.commons.codec.binary.Base64;
import org.springframework.util.Assert;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @author lwh
 * @date 2024/2/27
 * @description 非对称加密
 * <p>
 * RSA公钥/私钥/签名工具包
 * </p>
 * <p>
 * 字符串格式的密钥在未在特殊说明情况下都为BASE64编码格式<br/>
 * 由于非对称加密速度极其缓慢,一般文件不使用它来加密而是使用对称加密,<br/>
 * 非对称加密算法可以用来对对称加密的密钥加密,这样保证密钥的安全也就保证了数据的安全
 * <p>
 **/
public final class RSAInternalUtil {

    /**
     * 签名算法
     */
    private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";

    /**
     * 密钥大小
     */
    private static final int KEY_SIZE = 1024;

    /**
     * 不可实例化
     */
    private RSAInternalUtil() {
    }

    /**
     * 生成密钥对
     *
     * @return 密钥对
     */
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(KEY_SIZE, new SecureRandom());
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 公钥加密
     *
     * @param publicKey 公钥
     * @param data      数据
     * @return 加密后的数据
     */
    private static byte[] encrypt(PublicKey publicKey, byte[] data) {
        Assert.notNull(publicKey, "parameter [publicKey] must not be empty");
        Assert.notNull(data, "parameter [data] must not be empty");
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 公钥加密
     *
     * @param publicKey 公钥
     * @param text      字符串
     * @return Base64编码字符串
     */
    public static String encrypt(PublicKey publicKey, String text) {
        Assert.notNull(publicKey, "parameter [publicKey] must not be empty");
        Assert.notNull(text, "parameter [text] must not be empty");
        byte[] data = encrypt(publicKey, text.getBytes());
        return data != null ? Base64.encodeBase64String(data) : null;
    }

    /**
     * 公钥加密
     *
     * @param publicKey 公钥
     * @param data      数据
     * @return 加密后的数据
     */
    public static byte[] encrypt(String publicKey, byte[] data) throws InvalidKeySpecException, NoSuchAlgorithmException {
        Assert.notNull(publicKey, "parameter [publicKey] must not be empty");
        Assert.notNull(data, "parameter [data] must not be empty");
        PublicKey pubKey = _buildPublicKey(publicKey);
        return encrypt(pubKey, data);
    }

    /**
     * 公钥加密
     *
     * @param publicKey 公钥
     * @param text      字符串
     * @return Base64编码字符串
     */
    public static String encrypt(String publicKey, String text) throws InvalidKeySpecException, NoSuchAlgorithmException {
        Assert.notNull(publicKey, "parameter [publicKey] must not be empty");
        Assert.notNull(text, "parameter [text] must not be empty");
        PublicKey pubKey = _buildPublicKey(publicKey);
        return encrypt(pubKey, text);
    }


    /**
     * 私钥解密
     *
     * @param privateKey 私钥
     * @param data       数据
     * @return 解密后的数据
     */
    private static byte[] decrypt(PrivateKey privateKey, byte[] data) {
        Assert.notNull(privateKey, "parameter [privateKey] must not be empty");
        Assert.notNull(data, "parameter [data] must not be empty");
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 私钥解密
     *
     * @param privateKey 私钥
     * @param data       数据
     * @return 解密后的数据
     */
    private static byte[] decrypt(String privateKey, byte[] data) throws InvalidKeySpecException, NoSuchAlgorithmException {
        Assert.notNull(privateKey, "parameter [privateKey] must not be empty");
        Assert.notNull(data, "parameter [data] must not be empty");
        PrivateKey priKey = _buildPrivateKey(privateKey);
        return decrypt(priKey, data);
    }

    /**
     * 私钥解密
     *
     * @param privateKey 私钥
     * @param text       Base64编码字符串
     * @return 解密后的数据
     */
    public static String decrypt(String privateKey, String text) throws InvalidKeySpecException, NoSuchAlgorithmException {
        Assert.notNull(privateKey, "parameter [privateKey] must not be empty");
        Assert.notNull(text, "parameter [text] must not be empty");
        byte[] data = decrypt(privateKey, Base64.decodeBase64(text));
        return data != null ? new String(data) : null;
    }


    /**
     * 私钥对信息生成数字签名
     *
     * @param privateKey 私钥
     * @param data       已加密数据
     * @return 签名
     */
    private static String sign(PrivateKey privateKey, byte[] data) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Assert.notNull(privateKey, "parameter [privateKey] must not be empty");
        Assert.notNull(data, "parameter [data] must not be empty");
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(data);
        return Base64.encodeBase64String(signature.sign());
    }

    /**
     * 私钥对信息生成数字签名
     *
     * @param privateKey 私钥 Base64
     * @param text       已加密数据Base64
     * @return 签名
     */
    public static String sign(String privateKey, String text) throws NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, InvalidKeyException {
        Assert.notNull(privateKey, "parameter [privateKey] must not be empty");
        Assert.notNull(text, "parameter [text] must not be empty");
        PrivateKey priKey = _buildPrivateKey(privateKey);
        return sign(priKey, text.getBytes());
    }

    /**
     * @param publicKey 公钥
     * @param data      已加密数据
     * @param sign      数字签名
     * @return 校验结果
     */
    private static boolean verify(PublicKey publicKey, byte[] data, String sign) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Assert.notNull(publicKey, "parameter [publicKey] must not be empty");
        Assert.notNull(data, "parameter [data] must not be empty");
        Assert.notNull(sign, "parameter [sign] must not be empty");
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(data);
        return signature.verify(Base64.decodeBase64(sign));
    }

    /**
     * 签名校验
     *
     * @param publicKey 公钥 Base64
     * @param text      已加密数据 Base64
     * @param sign      数字签名
     * @return 校验结果
     */
    public static boolean verify(String publicKey, String text, String sign) throws NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, InvalidKeyException {
        Assert.notNull(publicKey, "parameter [publicKey] must not be empty");
        Assert.notNull(text, "parameter [text] must not be empty");
        Assert.notNull(sign, "parameter [sign] must not be empty");
        PublicKey pubKey = _buildPublicKey(publicKey);
        return verify(pubKey, text.getBytes(), sign);
    }

    /**
     * 私钥转换
     *
     * @param privateKey 私钥字符串
     * @return PrivateKey
     */
    private static PrivateKey _buildPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        Assert.notNull(privateKey, "parameter [privateKey] must not be empty");
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(pkcs8KeySpec);
    }

    /**
     * 公钥转换
     *
     * @param publicKey 公钥
     * @return 公钥
     */
    private static PublicKey _buildPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }


}

ZuulFilterHelper

import com.junyu.loancloud.response.ServerResponse;
import com.junyu.loancloud.utils.JsonUtil;
import com.netflix.zuul.context.RequestContext;

import javax.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;

  /**
     * @author lwh
     * @date 2024/2/28
     * @description 响应帮助类
     **/
@Slf4j
public class ZuulFilterHelper {
    /**
     * 拒绝访问
     */
    public static void accessDenied(ServerResponse serverResponse) {
        log.error("网关拒绝访问:{}", JsonUtil.obj2StringPretty(serverResponse));
        RequestContext requestContext = RequestContext.getCurrentContext();
        //得到response
        HttpServletResponse response = requestContext.getResponse();
        //拒绝访问
        requestContext.setSendZuulResponse(false);
        //设置响应代码
        requestContext.setResponseStatusCode(200);
        //转成json
        String jsonString = JsonUtil.objectToJson(serverResponse);
        requestContext.setResponseBody(jsonString);
        //转成json,设置contentType
        response.setContentType("application/json;charset=utf-8");
    }
}

HeaderMapRequestWrapper

可以通过httpServletRequestWrapper.addHeader();添加自定义header,向下游传递

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.*;

/**
 * @author lwh
 * @date 2024/1/16
 * @description 自定义对request设置header
 **/
public class HeaderMapRequestWrapper extends HttpServletRequestWrapper {


    public HeaderMapRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    private Map<String, String> headerMap = new HashMap<>();

    /**
     * @author lwh
     * @date 2024/1/16
     * @description 添加header
     **/
    public void addHeader(String name, String value) {
        headerMap.put(name, value);
    }

    /**
     * @author lwh
     * @date 2024/1/16
     * @description 获取指定的header
     **/
    @Override
    public String getHeader(String name) {
        String headerValue = super.getHeader(name);
        if (headerMap.containsKey(name)) {
            headerValue = headerMap.get(name);
        }
        return headerValue;
    }

    /**
     * @author lwh
     * @date 2024/1/16
     * @description 获取所有的header名称
     **/
    @Override
    public Enumeration<String> getHeaderNames() {
        List<String> names = Collections.list(super.getHeaderNames());
        for (String name : headerMap.keySet()) {
            names.add(name);
        }
        return Collections.enumeration(names);
    }

    /**
     * @author lwh
     * @date 2024/1/16
     * @description 获取数组类型的header
     **/
    @Override
    public Enumeration<String> getHeaders(String name) {
        List<String> values = Collections.list(super.getHeaders(name));
        if (headerMap.containsKey(name)) {
            values = Arrays.asList(headerMap.get(name));
        }
        return Collections.enumeration(values);
    }
}

CommonCode

public enum CommonCode{

    //公共部分
    SUCCESS(200, "成功"),
    CODE_00001(1001, "系统繁忙,请稍后重试!"),
    CODE_00002(1002, "参数异常"),
    CODE_00404(1003, "请求不存在!"),
    CODE_00405(1004, "验签失败!"),
    CODE_FAIL_1(-1, "");

    CommonCode(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    private Integer code;
    private String msg;

    @Override
    public Integer code() {
        return code;
    }

    @Override
    public String msg() {
        return msg;
    }
}

RequestUtil

package com.lwh.loancloud.utils;

import java.io.*;
import java.net.URLDecoder;
import java.util.*;

import javax.servlet.http.HttpServletRequest;

/**
 * request工具类
 */
public class RequestUtil {

    /**
     * 移除request指定参数
     *
     * @param request
     * @param paramName
     * @return
     */
    public String removeParam(HttpServletRequest request, String paramName) {
        String queryString = "";
        Enumeration keys = request.getParameterNames();
        while (keys.hasMoreElements()) {
            String key = (String) keys.nextElement();
            if (key.equals(paramName)) {
                continue;
            }
            if ("".equals(queryString)) {
                queryString = key + "=" + request.getParameter(key);
            } else {
                queryString += "&" + key + "=" + request.getParameter(key);
            }
        }
        return queryString;
    }

    /**
     * 获取请求basePath
     *
     * @param request
     * @return
     */
    public static String getBasePath(HttpServletRequest request) {
        StringBuffer basePath = new StringBuffer();
        String scheme = request.getScheme();
        String domain = request.getServerName();
        int port = request.getServerPort();
        basePath.append(scheme);
        basePath.append("://");
        basePath.append(domain);
        if ("http".equalsIgnoreCase(scheme) && 80 != port) {
            basePath.append(":").append(String.valueOf(port));
        } else if ("https".equalsIgnoreCase(scheme) && port != 443) {
            basePath.append(":").append(String.valueOf(port));
        }
        return basePath.toString();
    }

    /**
     * 获取ip工具类,除了getRemoteAddr,其他ip均可伪造
     *
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("Cdn-Src-Ip");    // 网宿cdn的真实ip
        if (ip == null || ip.length() == 0 || " unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");   // 蓝讯cdn的真实ip
        }
        if (ip == null || ip.length() == 0 || " unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");  // 获取代理ip
        }
        if (ip == null || ip.length() == 0 || " unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP"); // 获取代理ip
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP"); // 获取代理ip
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr(); // 获取真实ip
        }
        return ip;
    }

    /**
     * 获取head key
     *
     * @param request
     * @param key
     * @return
     */
    public static String getHeadParams(HttpServletRequest request, String key) {
        String token = request.getHeader(key);
        if (token == null) {
            token = request.getParameter(key);
            if (token == null) {
                return null;
            }
        }
        return token.trim();
    }

    /**
     * 从请求中获取body数据
     *
     * @param request
     * @return
     * @throws Exception
     */
    public static String getBodyString(HttpServletRequest request) throws Exception {
        request.setCharacterEncoding("UTF-8");
        String body = null;
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;

        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }

        body = stringBuilder.toString();
        return body;
    }

    /**
     * 请求中参数转Map<String, String>,for支付宝异步回调,平时建议直接使用request.getParameterMap(),返回Map<String, String[]>
     *
     * @param request
     * @return
     */
    public static Map<String, String> getParameterMap(HttpServletRequest request) {
        Map<String, String> result = new LinkedHashMap<>();
        Enumeration parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String parameterName = (String) parameterNames.nextElement();
            result.put(parameterName, request.getParameter(parameterName));
        }
        return result;
    }

    public static String buildUrlParameter(Map<String, String> dateArry) {
        StringBuilder postParameter = new StringBuilder();
        int postItemTotal = dateArry.keySet().size();
        int item = 0;
        for (String key : dateArry.keySet()) {
            postParameter.append(key).append("=").append(dateArry.get(key));
            item++;
            if (item < postItemTotal) {
                postParameter.append("&");
            }
        }
        return postParameter.toString();
    }

    /**
     * 转换返回参数处理
     *
     * @return
     * @throws Exception
     */
    public static Map<String, String> convertMap(String responseString) {
        Map<String, String> map = new TreeMap<String, String>();
        String[] listObj = responseString.split("&");
        for (String temp : listObj) {
            if (temp.matches("(.+?)=(.+?)")) {
                String[] tempListObj = temp.split("=");
                map.put(tempListObj[0], tempListObj[1]);
            } else if (temp.matches("(.+?)=")) {
                String[] tempListObj = temp.split("=");
                map.put(tempListObj[0], "");
            } else {
                throw new RuntimeException("参数无法分解!");
            }
        }
        return map;
    }

    /**
     * @author lwh
     * @date 2024/2/27
     * @description 获取有序的参数
     **/
    public static Map<String, String> getOrderedParams(HttpServletRequest request) throws UnsupportedEncodingException {
        Map<String, String> orderedParams = new LinkedHashMap<>();
        // 获取原始查询字符串
        String queryString = URLDecoder.decode(request.getQueryString(), "utf-8");
        if (queryString != null) {
            String[] pairs = queryString.split("&");
            for (String pair : pairs) {
                int idx = pair.indexOf("=");
                String key = idx > 0 ? pair.substring(0, idx) : pair;
                orderedParams.put(key, idx > 0 && pair.length() > idx + 1 ? pair.substring(idx + 1) : null);
            }
        }
        return orderedParams;
    }
}

ServerResponse

import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

import java.io.Serializable;

@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel(value = "服务响应", description = "通用服务响应结果")
public class ServerResponse<T> implements Serializable {
    /**
     * 成功or失败
     */
    @ApiModelProperty(value = "结果", example = "true")
    private boolean result;
    /**
     * 成功or失败
     */
    @ApiModelProperty(value = "结果", example = "true")
    private boolean success;
    /**
     * 状态
     */
    @ApiModelProperty(value = "状态码,成功:00000", example = "00000")
    private Integer code;
    /**
     * 描述
     */
    @ApiModelProperty(value = "描述,错误描述", example = "SUCCESS")
    private String message;

    /**
     * 响应时间
     */
    @ApiModelProperty(value = "响应时间")
    private long serverTime = System.currentTimeMillis();

    private T data;

    public ServerResponse() {
    }

    public ServerResponse(boolean result, Integer code, T data) {
        this.result = result;
        this.code = code;
        this.data = data;
    }

    public ServerResponse(Integer code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public ServerResponse(boolean result, Integer code, String message, T data) {
        this.result = result;
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public ServerResponse(boolean result, Integer code, String message) {
        this.result = result;
        this.code = code;
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }

    public T getData() {
        return data;
    }

    public String getMessage() {
        return message;
    }

    public static <T> ServerResponse<T> createBySuccess() {
        return new ServerResponse<T>(true, CommonCode.SUCCESS.code(), CommonCode.SUCCESS.msg());
    }


    public static <T> ServerResponse<T> createBySuccess(T data) {
        return new ServerResponse<T>(true, CommonCode.SUCCESS.code(), CommonCode.SUCCESS.msg(), data);
    }

    public static <T> ServerResponse<T> createBySuccess(String message, T data) {
        return new ServerResponse<T>(true, CommonCode.SUCCESS.code(), message, data);
    }

    public static <T> ServerResponse<T> createByError() {
        return new ServerResponse<T>(false, CommonCode.CODE_00001.code(), CommonCode.CODE_00001.msg());
    }

    public static <T> ServerResponse<T> createByErrorMessage(String message) {
        return new ServerResponse<T>(false, CommonCode.CODE_00001.code(), message);
    }

    public static <T> ServerResponse<T> createByErrorCodeMessage(Integer code, String message) {
        return new ServerResponse<T>(false, code, message);
    }

    public static <T> ServerResponse<T> createErrorByCode(ResultCode statusCode) {
        return new ServerResponse<T>(false, statusCode.code(), statusCode.msg());
    }


    public void setCode(Integer code) {
        this.code = code;
    }

    public void setData(T data) {
        this.data = data;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public void setResult(boolean result) {
        this.result = result;
    }

    public boolean isResult() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public boolean isSuccess() {
        return success;
    }

    public long getServerTime() {
        return serverTime;
    }

    public void setServerTime(long serverTime) {
        this.serverTime = serverTime;
    }

    @Override
    public String toString() {
        return "ServerResponse{" +
                "result=" + result +
                ", code='" + code + '\'' +
                ", data=" + data +
                ", message='" + message + '\'' +
                '}';
    }
}

解释

首先服务端要生成一对公钥和私钥,私钥给到客户端,公钥自己服务端保存,除了私钥外,还要提供appId和version给到客户端,客户端根据服务端生成签名内容的方式生成签名内容,之后服务端根据客户端传递过来的内容生成签名内容,后面根据签名内容和公钥校验签名sign,对于上传文件则不校验签名。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术武器库

一句真诚的谢谢,胜过千言万语

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值