赞
踩
需求描述:前端发起请求的参数携带sign=xxxx,后台验证签名是够正确
sign签名生成规则:
1.将post请求的body转成jsonstring (按照body里key的自然升序排列),得到stringA,即: “reqBody=
j
s
o
n
s
t
r
i
n
g
"
2.
对
字
符
串
s
t
r
i
n
g
A
=
"
r
e
q
B
o
d
y
=
{jsonstring}" 2.对字符串stringA="reqBody=
jsonstring"2.对字符串stringA="reqBody={jsonstring}”, stringB=“cpToken=
c
p
T
o
k
e
n
"
,
s
t
r
i
n
g
C
=
"
o
e
m
=
{cpToken}", stringC="oem=
cpToken",stringC="oem={oem}”, stringD=“oemId=
o
e
m
I
d
"
,
=
s
t
r
i
n
g
E
=
"
t
i
m
e
s
t
a
m
p
=
{oemId}",=stringE="timestamp=
oemId",=stringE="timestamp={timestamp}”, stringF=“source=${source}”,使用按照字符串的字母升序(a→z)进行排列,获得字符串stringG;、
3.stringG后拼接“&secretKey=Onlineradio”得到StringH
4.对stringH进行BASE64编码得到字符串stringI;
5.对stringI进行MD5运算,并将得到的字符串所有字符转换为大写,得到sign值。
项目结构
config类
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.web.servlet.config.annotation.InterceptorRegistration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @Author: 李浩真 */ public class config implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { //拦截 InterceptorRegistration registration = registry.addInterceptor(new HttpServletRequestReplacedInterceptor()); registration.excludePathPatterns("/error"); registration.addPathPatterns("/v1/**"); registry.addInterceptor(new HttpServletRequestReplacedInterceptor()).addPathPatterns("/{version}/**"); } @Bean public FilterRegistrationBean httpServletRequestReplacedRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new HttpServletRequestReplacedFilter()); registration.addUrlPatterns("/*"); registration.addInitParameter("paramName", "paramValue"); registration.setName("httpServletRequestReplacedFilter"); registration.setOrder(1); return registration; } }
HttpServletRequestReplacedFilter类
/** * 李浩真 */ @Component public class HttpServletRequestReplacedFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletRequest requestWrapper = null; if(request instanceof HttpServletRequest) { requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request); } //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。 // 在chain.doFiler方法中传递新的request对象 if(requestWrapper == null) { chain.doFilter(request, response); } else { chain.doFilter(requestWrapper, response); } } @Override public void init(FilterConfig arg0) throws ServletException { } }
HttpServletRequestReplacedInterceptor
/** * @author 李浩真 */ @Component public class HttpServletRequestReplacedInterceptor extends HandlerInterceptorAdapter { // 进入控制器之前 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //判断请求类型 if (request.getMethod().equals("GET")) { return doGet(request, response); } else if (request.getMethod().equals("POST")) { return doPost(request, response); } return false; } /** * GET请求 */ public Boolean doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { //从request获取到所有的参数及其值 String sign = request.getParameter("sign"); if (sign == null) { returnCode(request, response, sign); return false; } Enumeration<?> pNames = request.getParameterNames(); Map<String, Object> map = new HashMap<String, Object>(); while (pNames.hasMoreElements()) { String pName = (String) pNames.nextElement(); Object pValue = request.getParameter(pName); map.put(pName, pValue); } //移除 map.remove("sign"); String newSign = SignUtil.createSign(map); if (!newSign.equals(sign)) { returnCode(request, response, sign); } return true; } /** * POST请求 */ public Boolean doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { String bodyString = HttpHelper.getBodyString(request); net.sf.json.JSONObject jb = net.sf.json.JSONObject.fromObject(bodyString); Map<String, Object> map = (Map<String, Object>) jb; String sign = (String) map.get("sign"); //移除 map.remove("sign"); String newSign = SignUtil.createSign(map); if (!newSign.equals(sign)) { returnCode(request, response, sign); } return true; } /** * 验证签名方法:失败,返回false */ public boolean returnCode(HttpServletRequest request, HttpServletResponse response, String sign) throws IOException { OutputStream out = null; try { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); net.sf.json.JSONObject res = new JSONObject(); res.put("success", false); // res.put("status code", CommonErrorEnum.SIGNATURE_VERIFICATION_FAILED.getValue()); // res.put("message", CommonErrorEnum.SIGNATURE_VERIFICATION_FAILED.getShowMessage()); res.put("status code", "123321"); res.put("message", "验签失败"); out = response.getOutputStream(); out.write(res.toString().getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { out.flush(); out.close(); } return false; } }
RequestReaderHttpServletRequestWrapper
import com.example.demo.util.HttpHelper; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; public class RequestReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public RequestReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8")); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } }
HttpHelper
import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; /** * @author 李浩真 */ public class HttpHelper { public static String getBodyString(HttpServletRequest request) throws IOException { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = request.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } }
MD5Util
import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * @author 李浩真 * MD5工具 */ public class MD5Util { /** * 16进制的字符串数组 */ private final static String[] HEX_DIGITS_STRINGS = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; /** * 16进制的字符集 */ private final static char[] HEX_DIGITS_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; /** * MD5加密字符串 * * @param source 源字符串 * @return 加密后的字符串 */ public static String getMD5(String source) { String mdString = null; if (source != null) { try { mdString = getMD5(source.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return mdString; } /** * MD5加密以byte数组表示的字符串 * * @param source 源字节数组 * @return 加密后的字符串 */ public static String getMD5(byte[] source) { String s = null; final int temp = 0xf; final int arraySize = 32; final int strLen = 16; final int offset = 4; try { MessageDigest md = MessageDigest .getInstance("MD5"); md.update(source); byte[] tmp = md.digest(); char[] str = new char[arraySize]; int k = 0; for (int i = 0; i < strLen; i++) { byte byte0 = tmp[i]; str[k++] = HEX_DIGITS_CHAR[byte0 >>> offset & temp]; str[k++] = HEX_DIGITS_CHAR[byte0 & temp]; } s = new String(str); } catch (Exception e) { e.printStackTrace(); } return s; } /** * @param plainText 明文 * @return 32位密文 */ public static String getMD5New(String plainText) { String re_md5 = new String(); try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(plainText.getBytes()); byte b[] = md.digest(); int i; StringBuffer buf = new StringBuffer(""); for (int offset = 0; offset < b.length; offset++) { i = b[offset]; if (i < 0) { i += 256; } if (i < 16) { buf.append("0"); } buf.append(Integer.toHexString(i)); } re_md5 = buf.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return re_md5; } /** * * 获取文件的MD5值 * * @param file 目标文件 * @return MD5字符串 * @throws Exception */ public static String getFileMD5String(File file) throws Exception { String ret = ""; FileInputStream in = null; FileChannel ch = null; try { in = new FileInputStream(file); ch = in.getChannel(); ByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); MessageDigest messageDigest = MessageDigest.getInstance("MD5"); messageDigest.update(byteBuffer); ret = byteArrayToHexString(messageDigest.digest()); } catch (IOException e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (ch != null) { try { ch.close(); } catch (IOException e) { e.printStackTrace(); } } } return ret; } /** * * 获取文件的MD5值 * * @param fileName 目标文件的完整名称 * @return MD5字符串 * @throws Exception */ public static String getFileMD5String(String fileName) throws Exception { return getFileMD5String(new File(fileName)); } /** * 加密 * * @param source 需要加密的原字符串 * @param encoding 指定编码类型 * @param uppercase 是否转为大写字符串 * @return */ public static String MD5Encode(String source, String encoding, boolean uppercase) { String result = null; try { result = source; // 获得MD5摘要对象 MessageDigest messageDigest = MessageDigest.getInstance("MD5"); // 使用指定的字节数组更新摘要信息 messageDigest.update(result.getBytes(encoding)); // messageDigest.digest()获得16位长度 result = byteArrayToHexString(messageDigest.digest()); } catch (Exception e) { e.printStackTrace(); } return uppercase ? result.toUpperCase() : result; } /** * 转换字节数组为16进制字符串 * * @param bytes 字节数组 * @return */ private static String byteArrayToHexString(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); for (byte tem : bytes) { stringBuilder.append(byteToHexString(tem)); } return stringBuilder.toString(); } /** * * 将字节数组中指定区间的子数组转换成16进制字符串 * * @param bytes 目标字节数组 * @param start 起始位置(包括该位置) * @param end 结束位置(不包括该位置) * @return 转换结果 */ public static String bytesToHex(byte bytes[], int start, int end) { StringBuilder sb = new StringBuilder(); for (int i = start; i < start + end; i++) { sb.append(byteToHexString(bytes[i])); } return sb.toString(); } /** * 转换byte到16进制 * * @param b 要转换的byte * @return 16进制对应的字符 */ private static String byteToHexString(byte b) { int n = b; if (n < 0) { n = 256 + n; } int d1 = n / 16; int d2 = n % 16; return HEX_DIGITS_STRINGS[d1] + HEX_DIGITS_STRINGS[d2]; } /** * * 校验密码与其MD5是否一致 * * @param pwd 密码字符串 * @param md5 基准MD5值 * @return 检验结果 */ public static boolean checkPassword(String pwd, String md5) { return getMD5(pwd).equalsIgnoreCase(md5); } /** * * 校验密码与其MD5是否一致 * * @param pwd 以字符数组表示的密码 * @param md5 基准MD5值 * @return 检验结果 */ public static boolean checkPassword(char[] pwd, String md5) { return checkPassword(new String(pwd), md5); } /** * * 检验文件的MD5值 * @param file 目标文件 * @param md5 基准MD5值 * @return 检验结果 * @throws Exception */ public static boolean checkFileMD5(File file, String md5) throws Exception { return getFileMD5String(file).equalsIgnoreCase(md5); } /** * * 检验文件的MD5值 * @param fileName 目标文件的完整名称 * @param md5 基准MD5值 * @return 检验结果 * @throws Exception */ public static boolean checkFileMD5(String fileName, String md5) throws Exception { return checkFileMD5(new File(fileName), md5); } }
SignUtil
/** * @author 李浩真 * 签名与验签工具 */ @Slf4j public class SignUtil { public static String createSign(Map<String, Object> originMap) { if (originMap == null) { return null; } originMap = sortMapByKey(originMap); StringBuffer originStr = new StringBuffer(); for (Map.Entry<String, Object> entry : originMap.entrySet()) { originStr.append(entry.getKey() + "=" + entry.getValue()); originStr.append("&"); } originStr = originStr.append("secretKey=ProjectName"); return MD5Util.getMD5(Base64.getEncoder().encodeToString(originStr.toString().getBytes(StandardCharsets.UTF_8))); } public static Map<String, Object> sortMapByKey(Map<String, Object> map) { /** * 对Map对象的key升序(a->z)排列 */ if (map == null || map.isEmpty()) { return null; } Map<String, Object> sortMap = new TreeMap<>(Comparator.naturalOrder()); sortMap.putAll(map); return sortMap; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。