java HTTP簽名使用MD5加密程式碼
阿新 • • 發佈:2019-01-12
工具類
import org.apache.commons.codec.digest.DigestUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.*; /** * @author wangwei * @version v1.0.0 * @description 簽名 * @date 2019-01-12 */ @RefreshScope public class SignUtil { private static Logger logger = LoggerFactory.getLogger(SignUtil.class); public static SignUtil signUtil; // 對外的金鑰 // @Value("${sign-secret-secret}") public String secretkey = "mysecret123456"; /** 加密金鑰 */ //@Value("${sign.secret.appkey}") public String appkey = "mykey123456"; // 間隔時間 //@Value("${sign.timeout}") public int timeout = 1 * 30 * 1000; /** 加密金鑰 */ // private final static String APP_KEY = "mykey123456"; // public final static String SECRET_KEY = "mysecret123456"; /** 字元編碼 */ private final static String INPUT_CHARSET = "UTF-8"; // /** 超時時間 */ // private final static int TIME_OUT = 1 * 30 * 1000; public static SignUtil getInstance() { if(signUtil == null) { signUtil = new SignUtil(); } return signUtil; } /** * 請求引數Map轉換驗證Map * @param requestParams 請求引數Map * @param charset 是否要轉utf8編碼 * @return * @throws UnsupportedEncodingException */ public static Map<String,String> toVerifyMap(Map<String, String[]> requestParams, boolean charset) { Map<String,String> params = new HashMap<>(); for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } //亂碼解決,這段程式碼在出現亂碼時使用。如果mysign和sign不相等也可以使用這段程式碼轉化 if(charset) valueStr = getContentString(valueStr, INPUT_CHARSET); params.put(name, valueStr); } return params; } /** * 除去陣列中的空值和簽名引數 * @param sArray 簽名引數組 * @return 去掉空值與簽名引數後的新簽名引數組 */ public static Map<String, String> paraFilter(Map<String, String> sArray) { Map<String, String> result = new HashMap<>(); 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")) { continue; } result.put(key, value); } return result; } /** * 把陣列所有元素排序,並按照“引數=引數值”的模式用“&”字元拼接成字串 * @param params 需要排序並參與字元拼接的引數組 * @return 拼接後字串 */ public static String createLinkString(Map<String, String> params) { return createLinkString(params, false); } /** * 把陣列所有元素排序,並按照“引數=引數值”的模式用“&”字元拼接成字串 * @param params 需要排序並參與字元拼接的引數組 * @param encode 是否需要UrlEncode * @return 拼接後字串 */ public static String createLinkString(Map<String, String> params, boolean encode) { List<String> keys = new ArrayList<>(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 (encode) value = urlEncode(value, INPUT_CHARSET); if (i == keys.size() - 1) {//拼接時,不包括最後一個&字元 prestr = prestr + key + "=" + value; } else { prestr = prestr + key + "=" + value + "&"; } } return prestr; } /** * 編碼轉換 * @param content * @param charset * @return * @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 content * @param charset * @return */ private static String getContentString(String content, String charset) { if (charset == null || "".equals(charset)) { return new String(content.getBytes()); } try { return new String(content.getBytes("ISO-8859-1"), charset); } catch (UnsupportedEncodingException e) { throw new RuntimeException("指定的編碼集不對,您目前指定的編碼集是:" + charset); } } /** * URL轉碼 * @param content * @param charset * @return */ private static String urlEncode(String content, String charset) { try { return URLEncoder.encode(content, charset); } catch (UnsupportedEncodingException e) { throw new RuntimeException("指定的編碼集不對,您目前指定的編碼集是:" + charset); } } //TODO 簽名 /** * 生成要請求的簽名引數陣列 * @param sParaTemp 需要簽名的引數Map * @return 要請求的簽名引數陣列 */ public static Map<String, String> signMap(Map<String, String[]> sParaTemp) { //請求引數Map轉換驗證Map,並生成要請求的簽名引數陣列 return SignUtil.getInstance().sign(toVerifyMap(sParaTemp, false)); } /** * 生成要請求的簽名引數陣列 * @param sParaTemp 需要簽名的引數 * @return 要請求的簽名引數陣列 */ public Map<String, String> sign(Map<String, String> sParaTemp) { //時間戳加入簽名引數組中 sParaTemp.put("timestamp", String.valueOf(System.currentTimeMillis())); //除去陣列中的空值和簽名引數 Map<String, String> sPara = paraFilter(sParaTemp); //把陣列所有元素,按照“引數=引數值”的模式用“&”字元拼接成字串 String prestr = createLinkString(sPara); //生成簽名結果 String mysign = DigestUtils.md5Hex(getContentBytes(prestr + appkey, INPUT_CHARSET)); //簽名結果加入請求提交引數組中 sPara.put("sign", mysign); return sPara; } public static String getSignStr(Map<String, String> sParaTemp) { return SignUtil.getInstance().sign(sParaTemp).get("sign"); } /** * 生成要請求的簽名引數字串“引數=引數值”&連結 * @param sParaTemp 需要簽名的引數Map * @return 請求的簽名引數字串 */ public static String signStringMap(Map<String, String[]> sParaTemp) { //生成要請求的簽名引數陣列 Map<String, String> sign = signMap(sParaTemp); //生成要請求的簽名引數字串“引數=引數值”&連結 return createLinkString(sign, true); } /** * 生成要請求的簽名引數字串“引數=引數值”&連結 * @param sParaTemp 需要簽名的引數 * @return */ public static String signString(Map<String, String> sParaTemp) { //生成要請求的簽名引數陣列 Map<String, String> sign = SignUtil.getInstance().sign(sParaTemp); //生成要請求的簽名引數字串“引數=引數值”&連結 return createLinkString(sign, true); } //TODO 驗證簽名 /** * 根據反饋回來的資訊,生成簽名結果 * @param paramsMap 通知返回來的請求引數Map * @return 驗證結果 */ public static boolean verifyMap(Map<String, String[]> paramsMap) { //請求引數Map轉換驗證Map,並根據反饋回來的資訊,生成簽名結果 return SignUtil.getInstance().verify(toVerifyMap(paramsMap, false)); } /** * 根據反饋回來的資訊,生成簽名結果 * @param params 通知返回來的引數陣列 * @return 驗證結果 */ public boolean verify(Map<String, String> params) { String sign = ""; if (params.get("sign") != null) { sign = params.get("sign"); }else { logger.info("sign is null"); return false; } String timestamp = ""; if (params.get("time") != null) { timestamp = params.get("time"); }else { return false; } //過濾空值、sign Map<String, String> sParaNew = paraFilter(params); //獲取待簽名字串 String preSignStr = createLinkString(sParaNew); //獲得簽名驗證結果 String mysign = DigestUtils.md5Hex(getContentBytes(preSignStr + appkey, INPUT_CHARSET)); if (mysign.equals(sign)) { //是否超時 long curr = System.currentTimeMillis(); if ((curr - Long.valueOf(timestamp)) > timeout){ logger.info("api is time out" + curr); return false; } return true; } else { return false; } } }
介面控制器呼叫
import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; /** * @author wangwei * @version v1.0.0 * @description 測試簽名用 * @date 2019-01-12 */ @RestController @RequestMapping("/api/open/rest/{version}/signtest") public class SignTestController { /** * 模擬客戶端請求API介面 * @param request * @return */ @RequestMapping("send") public String send(HttpServletRequest request){ Map<String,String> param = new HashMap<>(); param.put("userId","9527"); param.put("amount","9.99"); param.put("productId","9885544154"); param.put("secretKey","mysecret123456"); try { String postResult = HttpClient.get("http://127.0.0.1:8773/api/open/v1.0.1/ad/list", SignUtil.getInstance().sign(param)); return postResult; } catch (Exception e) { e.printStackTrace(); } return "success"; } /** * showdoc * @catalog v1.0.1/測試相關 * @title 模擬服務的API介面 * @description 模擬服務的API介面 * @method post * @url /api/open/rest/v1.0.1/signtest/checkSign * @param secretKey 必選 string 祕鑰 * @param time 必選 string 請求時間戳 * @param sign 必選 string 引數MD5簽名 * @return {"status":"200","message":"請求成功","data":,"page":null,"ext":null} * @return_param code int 驗證碼 * @return_param status string 狀態 * @remark 這裡是備註資訊 * @number 99 */ @RequestMapping("checkSign") public Resp checkSign(HttpServletRequest request){ //從request中獲取引數列表,轉成map Map<String, String> map = SignUtil.toVerifyMap(request.getParameterMap(),false); String secretKey = map.get("secretKey"); if (StringUtils.isEmpty(secretKey) || !map.get("secretKey").equals(SignUtil.getInstance().secretkey)){ System.out.println("secretKey is err"); return Resp.fail(ErrorCode.CODE_431); } if (SignUtil.getInstance().verify(map)){ return Resp.success(); }else { return Resp.fail(ErrorCode.CODE_430); } } }
簽名驗證過濾器
import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.Map; /** * @author wangwei * @version v1.0.0 * @description 簽名過濾器, 簽名不攔截open下的介面 * @date 2019-01-12 */ @Component @WebFilter(filterName="SignFilter", urlPatterns="/*") @RefreshScope public class SignFilter implements Filter { @Value("${version}") private String version; @Override public void init(javax.servlet.FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); String path = request.getRequestURI(); if(path.indexOf("/api/open/")> -1 ){ filterChain.doFilter(servletRequest, servletResponse); return; } Map<String, String> map = SignUtil.toVerifyMap(request.getParameterMap(),false); String secretKey = map.get("secretKey"); if (StringUtils.isEmpty(secretKey) || !map.get("secretKey").equals(SignUtil.getInstance().secretkey)){ System.out.println("secretKey is err"); PrintWriter writer = null; servletResponse.setCharacterEncoding("UTF-8"); servletResponse.setContentType("text/html; charset=utf-8"); try { writer = servletResponse.getWriter(); String userJson = "{\"code\":\" "+ ErrorCode.CODE_431.getCode() +"\", \"message\": \""+ ErrorCode.CODE_431.getMessage() +"\"}"; writer.print(userJson); } catch (IOException e1) { } finally { if (writer != null) writer.close(); } } if (SignUtil.getInstance().verify(map)){ // 簽名成功 filterChain.doFilter(servletRequest, servletResponse); return; }else { PrintWriter writer = null; servletResponse.setCharacterEncoding("UTF-8"); servletResponse.setContentType("text/html; charset=utf-8"); try { writer = servletResponse.getWriter(); String userJson = "{\"code\":\" "+ ErrorCode.CODE_430.getCode() +"\", \"message\": \""+ ErrorCode.CODE_430.getMessage() +"\"}"; writer.print(userJson); } catch (IOException e1) { } finally { if (writer != null) writer.close(); } } } @Override public void destroy() { } }