java Http傳輸md5簽名工具類
阿新 • • 發佈:2019-02-09
從客戶端轉為服務端日記(一)
應用場景:
應用A請求應用B為保障資料不被非法篡改我們通常會對資料進行md5加密。
加密演算法流程:
1.加入時間戳引數
2.根據字典樹對請求的引數(Map<String,String>)進行氣泡排序。
3.對資料進行格式化==> A=a&B=b×tamp=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 } }