第三方调用安全校验

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>

 

posted @ 2017-11-29 21:06  Jtianlin  阅读(513)  评论(0编辑  收藏  举报