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; } }
划船不用桨、杨帆不等风、一生全靠浪