接口验签流程
- 接收前端传来的入参,并根据入参的key通过ASCII码进行排序(放到treeMap即可)
- 将排序好的json字符创进行md5摘要。(将sign字段去除)
- 将排序好的字符串与前端传入的sign字段进行比对,如果正确,继续,错误返回错误信息
存在的问题(body丢失,所有post请求失效)
- 从HttpServletRequest获取的InputStream读一次就无法再读了
解决办法:将inputStream通过 extends HttpServletRequestWrapper进行inputStream的包装,让inputStream从ByteArrayInputStream中进行流的读取,该对象可以从内存中进行数据的读取,不会读取一次之后就无法再读了。
1.请求体存在流中,保证后面可以读取到请求体,需要复制流
2.inputStream的读取方式为:会有一个pos指针,他指示每次读取之后下一次要读取的起始位置,当读到最后一个字符的时候,pos指针不会重置
3.但不是所有的流都是这种读取方式,ByteArrayInputStream 就是从内存中读取byte[],每次读取都会重新遍历。就像遍历List时,会新建一个
Iterator,所以pos也就回到了开始的位置。
- 未理解的点?
为什么在复制流的时候,要从流中read出来,在write出去,而不能直接使用httpservlet.getInputStream。
代码实现过程
- 实现过滤器接口
- 重写doFilter方法
- 新建类继承HttpServletRequestWrapper,Stream.copyInputStream。copy出一个字节数组。重写getInputStream()和getReader(),改为内存读取流,保证body不丢失
- 获取url上和body中的参数,并通过treemap进行排序
- 对map中的数据进行除了sign字段的摘要
- 与前端传入的md5摘要进行比对
代码实现
@Slf4j
@WebFilter(urlPatterns = {
"/user"})
public class SignAuthFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
String currentTime = allParams.get(Constant.System.SIGN_TIME);
if (StringUtils.isBlank(currentTime)) {
log.error("current_time字段为空,验签失败,param:{}", JSON.toJSONString(allParams));
HttpUtils.setResponsemessage(response, "参数异常");
return;
}
boolean isTimeOut = SignUtil.verifyStayTime(currentTime);
if (Boolean.FALSE.equals(isTimeOut)) {
log.error("页面停留时间过长,请重新提交,param:{}", JSON.toJSONString(allParams));
HttpUtils.setResponsemessage(response, "页面停留时间过长,请重新提交");
return;
}
boolean isSigned = SignUtil.verifySign(allParams);
if (isSigned) {
chain.doFilter(requestWrapper, response);
} else {
log.error("验签失败,param:{}", JSON.toJSONString(allParams));
HttpUtils.setResponsemessage(response, "参数异常");
}
}
@Override
public void