Web拦截器实现http访问方法和数字签名认证

Web拦截器实现http访问方法和数字签名认证

当有对外系统对接业务的时候,数字签名能很好的校验非法访问,本文提供拦截器实现验证数字签名的方法

1.在项目中把commons-codec-1.3jar放进去以支持MD5加密类

2.在web.xml中添加web拦截器

<filter>
        <filter-name>signFilter</filter-name>
        <filter-class>com.wanda.crs.filter.HttpRequestSignInputCheck</filter-class>
        <init-param>    
            <param-name>excludedPages</param-name>    
            <param-value>/damai/*,/index.jsp,/tableIndex.jsp,/table/*</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>signFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

3.编写拦截器的执行类HttpRequestSignInputCheck.java

package com.wanda.crs.filter;

import java.io.IOException;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;

import com.alibaba.fastjson.JSON;
import com.wanda.crs.standard.model.ClientInfoModel;
import com.wanda.crs.utils.Contants;
import com.wanda.crs.utils.HttpClientUtils;
import com.wanda.crs.utils.MyRequestUtil;
import com.wanda.crs.utils.PropConfigUtil;
import com.wanda.crs.utils.RenderHelper;
import com.wanda.crs.utils.SignCheck;
import com.wanda.crs.utils.StandardResult;
import com.wanda.crs.utils.StringUtil;
import com.wanda.crs.utils.httpclient.HttpRequest;

public class HttpRequestSignInputCheck implements Filter{

    private Logger _log = org.slf4j.LoggerFactory.getLogger(getClass());
    
    private static final String requestClientAppId = "clientId";
    
    /**
     * 是否是开发模式
     */
    private static final boolean devMode = true;
    
    private String excludedPages;  //不需要拦截的url
    private String[] excludedPagesArray;
    private static String api_url="";
    
    @Override
    public void destroy() {
        System.gc();
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)resp;
        
        boolean isExcludedPage = false;
        if(excludedPagesArray!=null&&excludedPagesArray.length>0){
            for (String page : excludedPagesArray) {  //不需要拦截的url 
                if(((HttpServletRequest) request).getServletPath().equals(page)){     
                    isExcludedPage = true;  
                    break; 
                }else if(page.contains("*")&&((HttpServletRequest) request).getServletPath().startsWith("/"+page.split("/")[1])){
                    isExcludedPage = true;  
                    break;
                }
            }
        }
        if (isExcludedPage) {//在过滤url之外     
            chain.doFilter(request, response);
        }else{
            if(!HttpRequest.METHOD_GET.equalsIgnoreCase(request.getMethod()) && 
                    !HttpRequest.METHOD_POST.equalsIgnoreCase(request.getMethod())){
                    StandardResult resultBase = new StandardResult();
                    StringBuilder errmsg = new StringBuilder();
                    errmsg.append("禁止[" + request.getMethod() + "]方法访问;允许的方法为[");
                    errmsg.append(HttpRequest.METHOD_GET + "]和[");
                    errmsg.append(HttpRequest.METHOD_POST + "]");
                    resultBase.setStatus(StandardResult.FAIL);
                    resultBase.setMessage(String.valueOf(errmsg));
                    RenderHelper.render(resultBase, response);
                    return;
                }
                Map<String, String> values = new HashMap<String, String>();
                if (HttpRequest.METHOD_GET.equals(request.getMethod())) {
                    values = MyRequestUtil.getParamsMap(request, "UTF-8");
                } else {
                    values = MyRequestUtil.getParamsMap(request,null);
                }
                String clientId = values.get(requestClientAppId);
                String channelKey = "";
                _log.debug("********HttpRequestSignInputCheck requestParams**********" + values);
                String queryString = SignCheck.createLinkString(values);
                _log.info("请求的URL:" + request.getRequestURL().toString() + "?" + queryString);
                /*******验证渠道合法性********/
                if (StringUtils.isBlank(clientId)) {
                    String respMsg = "clientId不能为空";
                    StandardResult resultBase = new StandardResult();
                    resultBase.setStatus(StandardResult.FAIL);
                    resultBase.setMessage(respMsg);
                    RenderHelper.render(resultBase, response);
                    return;
                }
                Map<String,String> params = new HashMap<String,String>();
                params.put("idClient", clientId);
                StandardResult result = HttpClientUtils.sendHttpMethod(api_url+"/product/getClientInfo", params, Contants.HTTP_METHOD_POST);
                if (result.getData()!=null&&!"null".equals(String.valueOf(result.getData()))&&StringUtil.isNotEmpty(String.valueOf(result.getData()))) {
                    ClientInfoModel clientInfo = JSON.parseObject(String.valueOf(result.getData()),ClientInfoModel.class);
                    if(clientInfo==null||StringUtil.isEmpty(clientInfo.getClientKey())){
                        String respMsg = "渠道商查询信息有误,请联系系统管理员!";
                        StandardResult resultBase = new StandardResult();
                        resultBase.setStatus(StandardResult.FAIL);
                        resultBase.setMessage(respMsg);
                        RenderHelper.render(resultBase, response);
                        return;
                    }
                    channelKey = clientInfo.getClientKey();
                }else{
                    result.setData("");
                    result.setCount(0);
                    /*result.setMessage(Contants.MSG_CHANNELID_ERROR);
                    result.setStatus(Contants.STATUS_CHANNELID_ERROR);*/
                    if(StringUtils.isNotBlank(result.getMessage())){
                        result.setMessage(result.getMessage());
                        result.setStatus(result.getStatus());
                    }else{
                        result.setMessage(Contants.MSG_CHANNELID_ERROR);
                        result.setStatus(Contants.STATUS_CHANNELID_ERROR);
                    }
                    RenderHelper.render(result, response);
                    return;
                }
                if(!devMode){
                    /**********验证签名**********/
                    boolean status = SignCheck.getSignVeryfy(values, values.get("sign"),channelKey);
                    if (!status) {
                        StandardResult resultBase = new StandardResult();
                        resultBase.setStatus(StandardResult.SIGN_ERROR);
                        resultBase.setMessage("签名验证失败");
                        RenderHelper.render(resultBase, response);
                        return;
                    }
                }
                
                _log.info("**********HttpRequestSignInputCheck finished***************");
                chain.doFilter(req,response);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        excludedPages = filterConfig.getInitParameter("excludedPages");     
        if(StringUtils.isNotEmpty(excludedPages)){
            excludedPagesArray = excludedPages.split(",");
        }
    }
    static{
        api_url = PropConfigUtil.getValueByKey("api.url");
    }
}

MD5工具类

SignCheck.java

package com.wanda.crs.utils;

import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wanda.crs.utils.httpclient.HttpProtocolHandler;
import com.wanda.crs.utils.httpclient.HttpRequest;
import com.wanda.crs.utils.httpclient.HttpResponse;
import com.wanda.crs.utils.httpclient.HttpResultType;

/** 
* 功能:MD5签名处理核心文件,不需要修改
* 版本:1.0
* 修改日期:2014-09-19
* 说明:
* */

public class SignCheck {
    
    private static Logger logger = LoggerFactory.getLogger(SignCheck.class);

    /**
     * 签名字符串
     * @param text 需要签名的字符串
     * @param key 密钥
     * @param input_charset 编码格式
     * @return 签名结果
     */
    public static String sign(String text, String key, String input_charset) {
        text = text + key;
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));
    }
    
    /**
     * 签名字符串
     * @param text 需要签名的字符串
     * @param sign 签名结果
     * @param key 密钥
     * @param input_charset 编码格式
     * @return 签名结果
     */
    public static boolean verify(String text, String sign, String key, String input_charset) {
        text = text + key;
        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
        logger.info("signed: " + mysign);
        if(mysign.equals(sign)) {
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * @param content
     * @param charset
     * @return
     * @throws SignatureException
     * @throws UnsupportedEncodingException 
     */
    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

    /** 
     * 除去数组中的空值和签名参数
     * @param sArray 签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {

        Map<String, String> result = new HashMap<String, String>();

        if (sArray == null || sArray.size() <= 0) {
            return result;
        }

        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
                || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }
        
        return result;
    }



    public static String createLinkString(Map<String, String> params) {

        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        String prestr = "";

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);

            if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }

        return prestr;
    }
    
    public static String sendPostInfo(Map<String, String> sParaTemp,
            String gateway, String key) throws Exception {
        // 待请求参数数组
        Map<String, String> sPara = buildRequestPara(sParaTemp, key);

        HttpProtocolHandler httpProtocolHandler = HttpProtocolHandler
                .getInstance();

        HttpRequest request = new HttpRequest(HttpResultType.BYTES);
        // 设置编码集
        request.setCharset("utf-8");

        request.setParameters(generatNameValuePair(sPara));
        request.setUrl(gateway);

        HttpResponse response = httpProtocolHandler.execute(request);
        if (response == null) {
            return null;
        }

        String strResult = response.getStringResult();

        return strResult;
    }
    
    public static Map<String, String> buildRequestPara(
            Map<String, String> sParaTemp, String key) {
        // 除去数组中的空值和签名参数
        Map<String, String> sPara = Core.paraFilter(sParaTemp);
        // 生成签名字符串
        String signString = SignCheck.createLinkString(sPara);
        String input_charset = sPara.get("_input_charset");
        if(StringUtils.isBlank(input_charset)){
            input_charset = "utf-8";
        }
        String mysign = SignCheck.sign(signString, key, input_charset);
        // 签名结果与签名方式加入请求提交参数组中
        sPara.put("sign", mysign);
//        sPara.put("sign_type", "MD5");

        return sPara;
    }
    
    /**
     * MAP类型数组转换成NameValuePair类型
     * 
     * @param properties
     *            MAP类型数组
     * @return NameValuePair类型数组
     */
    private static NameValuePair[] generatNameValuePair(
            Map<String, String> properties) {
        NameValuePair[] nameValuePair = new NameValuePair[properties.size()];
        int i = 0;
        for (Map.Entry<String, String> entry : properties.entrySet()) {
            nameValuePair[i++] = new NameValuePair(entry.getKey(),
                    entry.getValue());
        }

        return nameValuePair;
    }
    
    //默认的加密编码
    public static String DEFAULT_INPUT_CHARSET = "UTF-8";
    
    /**
     * 根据传入的信息,生成签名结果
     * @param Params 传入的参数数组
     * @param sign 比对的签名结果
     * @return 生成的签名结果
     * @throws Exception 
     */
    public static boolean getSignVeryfy(Map<String, String> Params, String sign, String channelKey) {
        boolean flag = true;
        //过滤空值、sign与sign_type参数
        Map<String, String> sParaNew = paraFilter(Params);
        //获取待签名字符串
        String preSignStr = createLinkString(sParaNew);
        //获取签名字符集
        String input_charset = Params.get("_input_charset");
        if (StringUtils.isEmpty(input_charset)) {
            input_charset = DEFAULT_INPUT_CHARSET;
        }
        //获得签名验证结果        
        boolean isSign = false;
        logger.info("签名之前字符串:" + preSignStr);
        isSign = verify(preSignStr, sign, channelKey, input_charset);
        if (!isSign) {
            flag = false;
        }
        
        return flag;
    }
    
    /**
     * 根据传入参数获取签名字符串
     * @param params
     * @return
     */
    public static String getSignByMap(Map<String,String> params,String key){
        //过滤空值、sign与sign_type参数
        Map<String, String> sParaNew = paraFilter(params);
        //获取待签名字符串
        String text = createLinkString(sParaNew);
        //获取签名字符集
        String input_charset = "UTF-8";
        
        text = text + key;
        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
        
        return mysign;
    }
}

 

posted @ 2017-08-17 16:50  十月围城小童鞋  阅读(771)  评论(0编辑  收藏  举报