java Http传输md5签名工具类

从客户端转为服务端日记(一)

应用场景:

应用A请求应用B为保障数据不被非法篡改我们通常会对数据进行md5加密。

加密算法流程:

1.加入时间戳参数

2.根据字典树对请求的参数(Map<String,String>)进行冒泡排序。

3.对数据进行格式化==> A=a&B=b&timestamp=121364565。

4.对格式化后的参数进行加密并加在格式化参数的末尾

验证算法流程

1.取出Map中的sigin

2.直接将map格式化并加密。比较md5是否一致


我将这个类用于拦截器中。当配置的请求非法时自动返回错误信息。


 

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class SignedRequestHelperTest {

	private static final String UTF8 = "UTF-8";
	private static final String HMAC_SHA256 = "HmacSHA256";

	private String secretKey = "myscretKey";

	private Mac mac;
	private String urlParams;

	public SignedRequestHelperTest(){
		init();
	}

	private void init() {
		try {
			byte[] secretyKeyBytes = secretKey.getBytes(UTF8);
			SecretKeySpec secretKeySpec = new SecretKeySpec(secretyKeyBytes, HMAC_SHA256);
			mac = Mac.getInstance(HMAC_SHA256);
			mac.init(secretKeySpec);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * @param params
	 * @return 没有urlecode的签名 params中sign是urlecode的签名
	 */
	public String sign(Map<String, String> params) {
		if (!params.containsKey("timestamp")) {
			params.put("timestamp", timestamp());
		}
		SortedMap<String, String> sortedParamMap = new TreeMap<String, String>(params);
		String canonicalQS = canonicalize(sortedParamMap);
		String toSign = canonicalQS;
		System.out.println(toSign);
		String hmac = hmac(toSign);
		String signed = percentEncodeRfc3986(hmac);
		params.put("sign", signed);
		sortedParamMap = new TreeMap<String, String>(params);
		this.setUrlParams(canonicalize(sortedParamMap));
		return signed;
	}

	public boolean validSign(Map<String, String> params) {
		if (params.containsKey("sign")) {
			String sign1 = params.get("sign");
			params.remove("sign");
			String sign2 = sign(params);
			//System.out.println(sign1);
			//System.out.println(sign2);
			if (sign2.equals(sign1)) {
				return true;
			}
		}
		return false;
	}

	private String hmac(String stringToSign) {
		String signature = null;
		byte[] data;
		byte[] rawHmac;
		try {
			data = stringToSign.getBytes(UTF8);
			rawHmac = mac.doFinal(data);
			Base64 encoder = new Base64();
			signature = new String(encoder.encode(rawHmac));
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(UTF8 + " is unsupported!", e);
		}
		return signature;
	}

	/**
	 * @return
	 */
	private String timestamp() {
		return System.currentTimeMillis() + "";
	}

	/**
	 * @param sortedParamMap
	 * @return
	 */
	private String canonicalize(SortedMap<String, String> sortedParamMap) {
		if (sortedParamMap.isEmpty()) {
			return "";
		}
		StringBuffer buffer = new StringBuffer();
		Iterator<Map.Entry<String, String>> iter = sortedParamMap.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry<String, String> kvpair = iter.next();
			buffer.append(percentEncodeRfc3986(kvpair.getKey()));
			buffer.append("=");
			buffer.append(percentEncodeRfc3986(kvpair.getValue()));
			if (iter.hasNext()) {
				buffer.append("&");
			}
		}
		String canonical = buffer.toString();
		return canonical;
	}

	/**
	 * Rfc3986</br>
	 * 此处建议使用spring的encodeUri方法
	 * http://docs.spring.io/spring/docs/4.0.x/javadoc-api/org/springframework/
	 * web/util/UriUtils.html
	 * 
	 * @param s
	 * @return
	 */
	private String percentEncodeRfc3986(String s) {
		String out;
		try {
			out = URLEncoder.encode(s, UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
		} catch (UnsupportedEncodingException e) {
			out = s;
		}
		return out;
	}

	public String getUrlParams() {
		return urlParams;
	}

	public void setUrlParams(String urlParams) {
		this.urlParams = urlParams;
	}

	public static void main(String[] args) throws Exception {
		SignedRequestHelperTest signReqHelper = new SignedRequestHelperTest();
		Map<String, String> params = new HashMap<String, String>();
		params.put("uname", "username");
		params.put("passwd", "password");
		params.put("a", "a");
		System.out.println(signReqHelper.sign(params));
		System.out.println(signReqHelper.validSign(params));
	}
}

 

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
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 com.fh.controller.base.BaseController;
import com.fh.util.SignedRequestHelper;


public class MD5Filter extends BaseController implements Filter {

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		
	}

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		logBefore(logger, "验证签名开始");
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		Enumeration e = (Enumeration) request.getParameterNames();
		Map<String, String> map = new HashMap();
		while (e.hasMoreElements()) {
			String key = (String)e.nextElement();
			map.put(key, request.getParameter(key));
		}
		
		try {
			SignedRequestHelper sign = new SignedRequestHelper();
			System.out.println(sign.validSign(map));
			if(sign.validSign(map)){
				chain.doFilter(req, res); // 调用下一过滤器
				return;
			}
		} catch (Exception e2) {
			// TODO: handle exception
			logBefore(logger, "验证签名异常");
		}
		response.setHeader("Content-type", "text/html;charset=UTF-8"); 
		response.setCharacterEncoding("utf-8");
		PrintWriter out = response.getWriter();
		

		String reString = "{\"errcode\":10003,\"data\":{},\"errmsg\":\"签名错误\"}";
	
		out.write(reString);
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub
		
	}
	
	
}


 

posted @ 2016-01-14 03:12  leejuen  阅读(1400)  评论(0编辑  收藏  举报