• java 过滤器 接口(API)验证入参,验签(sign) Demo


    java 过滤器 接口(API)验证入参,验签(sign) Demo

    一、思路
    1、配置yml文件;
    2、创建加载配置文件类;
    3、继承 OncePerRequestFilter 重写方法 doFilterInternal;
    4、注册自定义过滤器;


    二、步骤

    1、配置yml文件;
    ###系统签名验证配置
    biw:
      ###过滤器开关是否打开
      enable: true
      ###过滤器-验签秘钥(自定义一个字符串)
      securityKey: ccf12f15155c9c564daf1783a6f65f69a4a0
      ###过滤器-URL
      urlPathPatterns: /test/test001/*,/com/baidu006/*

    2、创建加载配置文件类;

    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    /**
     * @Author 
     * @create 2023/07/18
     */
    @Data
    @Component
    @ConfigurationProperties(prefix = "biw")
    public class BiwConfig {
    
        /**
         * 系统通讯密钥
         */
        private String securityKey;
    
        /**
         * 签名验证URL路径
         */
        private String urlPathPatterns;
    
        /**
         * 是否开启签名验证
         */
        private Boolean enable;
    
    }
    

    3、继承 OncePerRequestFilter 重写方法 doFilterInternal;

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.test.baidu.ResultCodeEnum;
    import com.test.baidu.config.BiwConfig;
    import com.test.baidu.common.model.Result;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.ServletRequest;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.nio.charset.Charset;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.TreeMap;
    
    /**
     * BIW系统接口鉴权过滤器
     *
     * @Author 
     * @Date 2023/07/18
     */
    @Slf4j
    @Component
    public class BiwSignFilter extends OncePerRequestFilter {
    
        @Autowired
        private BiwConfig biwConfig;
    
        private final String SIGN_FIELD_NAME = "sign";
        private final String KEY_FIELD_NAME = "key";
    
    
        /**
         * doFilterInternal
         *
         * @param request
         * @param response
         * @param filterChain
         * @throws ServletException
         * @throws IOException
         */
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
            try {
                ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);
    
                // 判断签名验证开关是否开启
                if (!biwConfig.getEnable()) {
                    filterChain.doFilter(requestWrapper, response);
                    return;
                }
    
                String bodyText = this.readHttpBody(requestWrapper);
                log.info("[系统接口鉴权]body内容: {}", bodyText);
                JSONObject jsonBody = JSONObject.parseObject(bodyText);
                Object signRequest = jsonBody.get(SIGN_FIELD_NAME);
                if (biwConfig.getEnable() && null == signRequest) {
                    log.error("签名信息不存在");
                    this.doReturn(response, Result.createError(ResultCodeEnum.SIGN_NOT_EXISTS.getCode(), ResultCodeEnum.SIGN_NOT_EXISTS.getMessage()));
                    return;
                }
                String sign = this.signMD5(jsonBody);
                // 验证签名
                if (biwConfig.getEnable() && !sign.equals(signRequest)) {
                    log.error("签名验证失败, 签名计算值 {} 签名请求值{} body内容{}", sign, signRequest, bodyText);
                    this.doReturn(response, Result.createError(ResultCodeEnum.SIGN_ERROR.getCode(), ResultCodeEnum.SIGN_ERROR.getMessage()));
                    return;
                }
    
                filterChain.doFilter(requestWrapper, response);
    
            } catch (Exception e) {
                log.error("签名验证异常", e);
                this.doReturn(response, Result.create500Error(e.getMessage()));
                return;
            }
        }
    
    
        /**
         * readHttpBody
         *
         * @param requestWrapper
         * @return
         * @throws IOException
         */
        private String readHttpBody(ServletRequest requestWrapper) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(requestWrapper.getInputStream(), Charset.forName("UTF-8")));
            String line = "";
            StringBuilder sb = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        }
    
    
        /**
         * doReturn
         *
         * @param response
         * @param result
         * @throws IOException
         */
        private void doReturn(HttpServletResponse response, Result result) throws IOException {
            ServletOutputStream out = response.getOutputStream();
            out.write(JSON.toJSONString(result).getBytes());
            out.flush();
        }
    
    
        /**
         * signMD5 : MD5签名加密
         *
         * @param jsonObject
         * @return
         */
        public String signMD5(JSONObject jsonObject) {
            Iterator it = jsonObject.getInnerMap().keySet().iterator();
            Map map = new TreeMap();
            StringBuilder signSb = new StringBuilder();
            while (it.hasNext()) {
                Object key = it.next();
                Object value = jsonObject.get(key);
                if (SIGN_FIELD_NAME.equals(key)) {
                    continue;
                }
                map.put(key.toString(), value);
            }
            for (Map.Entry entry : map.entrySet()) {
                signSb.append(entry.getKey());
                signSb.append("=");
                signSb.append(entry.getValue());
                signSb.append("&");
            }
            signSb.append(KEY_FIELD_NAME).append("=").append(biwConfig.getSecurityKey());
            String sign = DigestUtils.md5Hex(signSb.toString()).toUpperCase();
            return sign;
        }
    
        /**
         * 生成签名
         */
        public static void main(String[] args) {
            String json = "{\"endTime\":\"2023-07-01 08:00:00\",\"startTime\":\"2023-07-01 00:00:00\",\"pageNum\":1,\"pageSize\":50,\"requestId\":\"test001\"}";
            JSONObject jsonObject = JSON.parseObject(json);
            Iterator it = jsonObject.getInnerMap().keySet().iterator();
            Map map = new TreeMap();
            StringBuilder signSb = new StringBuilder();
            while (it.hasNext()) {
                Object key = it.next();
                Object value = jsonObject.get(key);
                if ("sign".equals(key)) {
                    continue;
                }
                map.put(key.toString(), value);
            }
            for (Map.Entry entry : map.entrySet()) {
                signSb.append(entry.getKey());
                signSb.append("=");
                signSb.append(entry.getValue());
                signSb.append("&");
            }
            signSb.append("key").append("=").append("ccf12f15155c9c564daf1783a6f65f69a4a0");
            String sign = DigestUtils.md5Hex(signSb.toString()).toUpperCase();
            System.out.println(sign);
        }
    
    }
    

    4、注册自定义过滤器;

    import com.test.baidu.config.BiwConfig;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @Author 
     * @date 2023/07/18
     * @description: 系统过滤配置
     */
    @Configuration
    public class BiwFilterConfig {
    
        @Autowired
        private BiwSignFilter biwSignFilter;
        @Autowired
        private BiwConfig biwConfig;
    
    
        /**
         * biwBillPullFilterConfig
         * 数据-签名过滤
         *
         * @return
         */
        @Bean
        public FilterRegistrationBean biwBillPullFilterConfig() {
            FilterRegistrationBean registration = new FilterRegistrationBean<>();
            // 注册自定义过滤器
            registration.setFilter(biwSignFilter);
            // 过滤所有路径
            // registration.addUrlPatterns(biwConfig.getUrlPathPatterns().split(","));
            registration.addUrlPatterns(biwConfig.getUrlPathPatterns());
            // 过滤器名称
            registration.setName("biwParametersFilter");
            // 优先级,越低越优先
            registration.setOrder(1);
            return registration;
        }
    
    }
    

    5、每次调用此方法时将数据流中的数据读取出来,然后再回填到InputStream之中

    解决通过@RequestBody和@RequestParam(POST方式)读取一次后控制器拿不到参数问题

    import org.apache.commons.io.IOUtils;
    
    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.*;
    
    
    /**
     * @Author 
     * @create 2023/07/18
     */
    public class RequestWrapper extends HttpServletRequestWrapper {
    
        private byte[] requestBody;
    
        private HttpServletRequest request;
    
        public RequestWrapper(HttpServletRequest request) throws IOException {
            super(request);
            this.request = request;
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            /**
             * 每次调用此方法时将数据流中的数据读取出来,然后再回填到InputStream之中
             * 解决通过@RequestBody和@RequestParam(POST方式)读取一次后控制器拿不到参数问题
             */
            if (null == this.requestBody) {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                IOUtils.copy(request.getInputStream(), baos);
                this.requestBody = baos.toByteArray();
            }
    
            final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
            return new ServletInputStream() {
    
                @Override
                public boolean isFinished() {
                    return false;
                }
    
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setReadListener(ReadListener listener) {
    
                }
    
                @Override
                public int read() {
                    return bais.read();
                }
            };
        }
    
        public byte[] getRequestBody() {
            return requestBody;
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(this.getInputStream()));
        }
    }
    

  • 相关阅读:
    五线谱与简谱有什么区别 五线谱简谱混排怎么打 吉他谱软件哪个好
    Pymoo:优化算法收敛性的实例分析
    CopyOnWriteArrayList解析
    Error: Port Library failed to initialize: -86
    【Pytest实战】pytest 基本概念及使用大全
    duilib 入门基础十四 菜单
    一文1500字手把手教你Jmeter如何压测数据库【保姆级教程】
    数据结构(8)树形结构——B树、B+树(含完整建树过程)
    基于FPGA的图像拉普拉斯变换实现,包括tb测试文件和MATLAB辅助验证
    java完整面试复习
  • 原文地址:https://blog.csdn.net/java_2017_csdn/article/details/132691537