zoukankan      html  css  js  c++  java
  • 第三方调用安全校验

    1. 拦截器代码

    /**
     * @Description 添加请求是否合法验证拦截器
     * @author 田林(lin.tian@mljr.com)
     * @date 2017年12月1日 下午4:20:38
     */
    @Component("signature")
    public class SignatureFilter implements Filter {
        
        private Logger logger = LoggerFactory.getLogger(SignatureFilter.class);
        
        @Value("${rsaKey}")
        private String rsaKey;
        
        public final static String JSON_PARAMS_TYPE="application/json";
        
        public final static String FORM_PARAMS_TYPE="application/x-www-form-urlencoded";
    
        @Override 
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            
            Map<String,String> singleValueMap = Maps.newHashMap();
            
            HttpServletRequest httpRequest=(HttpServletRequest)request;
            
            String password = httpRequest.getHeader("password");
            
            // 是否通过验证
            boolean flag = false;
            
            //时间戳
            String timestamp = httpRequest.getHeader("timestamp");
            String serverPath = httpRequest.getServletPath();
            
            if("/".equals(serverPath)||serverPath.contains("/fe-che-union/static")){
                chain.doFilter(request,response);
            }else{
                HttpServletResponse httpResponse= (HttpServletResponse) response;
                String contentType = httpRequest.getContentType();
                if(!StringUtils.isEmpty(contentType)&&contentType.contains(JSON_PARAMS_TYPE)){//如果是json请求方式
                    SignatureRequestWrapper requestWrapper = new SignatureRequestWrapper(httpRequest);
                    String body = HttpHelper.getBodyString(requestWrapper);
                    if (StringUtils.isEmpty(body)) {
                        logger.error("非法请求, 无参数");
                        OutWriterUtil.write(httpResponse, JSONObject.toJSONString(RespDTO.fail("无参数")));
                        return;
                    }
                    Map<String, Object> parameters = JSONObject.parseObject(body);
                    
                    Set<String> keySet = parameters.keySet();
                    for(String key:keySet){
                        singleValueMap.put(key, JSONObject.toJSONString(parameters.get(key)));
                    }
    
                    request=requestWrapper;
                }else if(!StringUtils.isEmpty(contentType)&&contentType.contains(FORM_PARAMS_TYPE)){
                   Map<String,String[]> parameterMap = request.getParameterMap();
                   for(String key : parameterMap.keySet()){
                       String[] valueArray = parameterMap.get(key);
         
                       if(valueArray!=null&&valueArray.length>0){
                           singleValueMap.put(key, valueArray[0]);
                       }
                   }
                } else {
                    flag = true;
                }
                
                if(flag){
                    chain.doFilter(request,response);
                }else{
                    
                    // 校验参数合法性
                    flag = SignatureUtils.checkSign(singleValueMap, rsaKey,password,timestamp);
                    
                    if(flag){
                        chain.doFilter(request,response);
                    }else{
                        OutWriterUtil.write(httpResponse, JSONObject.toJSONString(RespDTO.fail("签名错误必须存在")));
                        return;
                    }
                }
            }
        }
    
        private boolean checkSign(Map<String, Object> parameters) {
            Map<String, String> requestParams=new HashMap<>();
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            for (String key : parameters.keySet()) {
                String valueStr="";
                Object value = parameters.get(key);
                if (value ==null){
                    continue;
                }
                if(BeanUtils.isSimpleValueType(value.getClass())){ //如果是简单类型
                    if(Date.class.isAssignableFrom(value.getClass())){//如果是时间类型
                        valueStr = dateFormat.format(value);
                    }else{
                        valueStr=value.toString();
                    }
                }else {
                    //如果是复杂类型
                    valueStr=JSONObject.toJSONString(value);
                }
                requestParams.put(key,valueStr);
            }
            return SignUtils.checkSign(requestParams,rsaKey);
        }
    
        private boolean checkParamIsExist(Map<String, Object> parameters, String... keys) {
            for (String key: keys) {
                if(parameters.get(key)==null){return false;}
            }
    
            return true;
        }
    
        @Override
        public void destroy() {
    
        }
        
    
    }

    2. 对输入流进行封装:

    public class SignatureRequestWrapper extends HttpServletRequestWrapper {
    
        private HttpServletRequest original;
        private byte[] reqBytes;
        private boolean firstTime = true;
    
        public SignatureRequestWrapper(HttpServletRequest request) {
            super(request);
            reqBytes = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
        }
    
        @Override
        public BufferedReader getReader() throws IOException{
            InputStreamReader isr = new InputStreamReader(new ByteArrayInputStream(reqBytes));
            return new BufferedReader(isr);
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
    
    
            ServletInputStream sis = new ServletInputStream() {
                @Override public boolean isFinished() {
                    return false;
                }
    
                @Override public boolean isReady() {
                    return false;
                }
    
                @Override public void setReadListener(ReadListener readListener) {
    
                }
    
                private int i;
    
                @Override
                public int read() throws IOException {
                    byte b;
                    if(reqBytes.length > i){
                        b = reqBytes[i++];
                    }else{
                        b = -1;
                    }
                    return b;
                }
            };
    
            return sis;
        }
    
    
    }

    3. 校验代码

    public class SignUtils {
    
        /**
         * 拼接键值对
         *
         * @param key
         * @param value
         * @param isEncode
         * @return
         */
        private static String buildKeyValue(String key, String value, boolean isEncode) {
            StringBuilder sb = new StringBuilder();
            sb.append(key);
            sb.append("=");
            if (isEncode) {
                try {
                    sb.append(URLEncoder.encode(value, "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    sb.append(value);
                }
            } else {
                sb.append(value);
            }
            return sb.toString();
        }
    
        /**
         * 对支付参数信息进行签名
         *
         * @param map
         *            待签名授权信息
         *
         * @return
         */
        public static String sign(Map<String, String> map, String rsaKey) {
            StringBuilder sortStr = getSortStr(map);
    
            StringBuilder authInfo = getAuthInfo(rsaKey, sortStr);
            String sign = EncryptUtil.MD5(authInfo.toString());
            return  sign;
        }
    
        private static StringBuilder getAuthInfo(String rsaKey, StringBuilder sortStr) {
            StringBuilder authInfo=new StringBuilder();
            authInfo.append(rsaKey);
            authInfo.append(sortStr);
            authInfo.append(rsaKey);
            return authInfo;
        }
    
        private static StringBuilder getSortStr(Map<String, String> map) {
            List<String> keys = new ArrayList<String>(map.keySet());
            // key排序
            Collections.sort(keys);
    
            StringBuilder authInfo = new StringBuilder();
            for (int i = 0; i < keys.size() - 1; i++) {
                String key = keys.get(i);
                String value = map.get(key);
                authInfo.append(buildKeyValue(key, value, false));
                authInfo.append("&");
            }
    
            String tailKey = keys.get(keys.size() - 1);
            String tailValue = map.get(tailKey);
            authInfo.append(buildKeyValue(tailKey, tailValue, false));
            return authInfo;
        }
    
        /**
         * 要求外部订单号必须唯一。
         * @return
         */
        public static boolean checkSign(Map<String, String> map, String rsaKey) {
    
            String password=map.remove("password");
    
            StringBuilder sortStr = getSortStr(map);
    
            StringBuilder authInfo = getAuthInfo(rsaKey, sortStr);
    
            return EncryptUtil.checkPassWord(authInfo.toString(),password);
    
    
    
        }

    4. Filter 无法直接通过 @Value 注入properties属性 ,可以通过 DelegatingFilterProxy 来处理,将Filter 交给spring来管理。web.xml 配置:

    <filter>
            <filter-name>signature</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>signature</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
  • 相关阅读:
    Qt学习之系列[9] – QCoreApplication:processEvents()可能会引起递归,导致栈溢出崩溃
    Qt中利用QTime类来控制时间,这里简单介绍一下QTime的成员函数的用法:
    获取输入设备的vid和pid
    QProcess 进程类—调用外部程序
    Q_INVOKABLE与invokeMethod用法全解
    QML插件扩展2(基于C++的插件扩展)
    leetcode第一刷_Word Search
    设计模式之抽象工厂模式
    Python Random随机数
    【X240 QQ视频对方听不到声音】解决方法
  • 原文地址:https://www.cnblogs.com/Jtianlin/p/7922732.html
Copyright © 2011-2022 走看看