java 过滤器 接口(API)验证入参,验签(sign) Demo
一、思路
1、配置yml文件;
2、创建加载配置文件类;
3、继承 OncePerRequestFilter 重写方法 doFilterInternal;
4、注册自定义过滤器;
二、步骤
1、配置yml文件;
###系统签名验证配置
biw:
###过滤器开关是否打开
enable: true
###过滤器-验签秘钥(自定义一个字符串)
securityKey: ccf12f15155c9c564daf1783a6f65f69a4a0
###过滤器-URL
urlPathPatterns: /test/test001/*,/com/baidu006/*
2、创建加载配置文件类;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Author
* @create 2023/07/18
*/
@Data
@Component
@ConfigurationProperties(prefix = "biw")
public class BiwConfig {
/**
* 系统通讯密钥
*/
private String securityKey;
/**
* 签名验证URL路径
*/
private String urlPathPatterns;
/**
* 是否开启签名验证
*/
private Boolean enable;
}
3、继承 OncePerRequestFilter 重写方法 doFilterInternal;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.test.baidu.ResultCodeEnum;
import com.test.baidu.config.BiwConfig;
import com.test.baidu.common.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
/**
* BIW系统接口鉴权过滤器
*
* @Author
* @Date 2023/07/18
*/
@Slf4j
@Component
public class BiwSignFilter extends OncePerRequestFilter {
@Autowired
private BiwConfig biwConfig;
private final String SIGN_FIELD_NAME = "sign";
private final String KEY_FIELD_NAME = "key";
/**
* doFilterInternal
*
* @param request
* @param response
* @param filterChain
* @throws ServletException
* @throws IOException
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);
// 判断签名验证开关是否开启
if (!biwConfig.getEnable()) {
filterChain.doFilter(requestWrapper, response);
return;
}
String bodyText = this.readHttpBody(requestWrapper);
log.info("[系统接口鉴权]body内容: {}", bodyText);
JSONObject jsonBody = JSONObject.parseObject(bodyText);
Object signRequest = jsonBody.get(SIGN_FIELD_NAME);
if (biwConfig.getEnable() && null == signRequest) {
log.error("签名信息不存在");
this.doReturn(response, Result.createError(ResultCodeEnum.SIGN_NOT_EXISTS.getCode(), ResultCodeEnum.SIGN_NOT_EXISTS.getMessage()));
return;
}
String sign = this.signMD5(jsonBody);
// 验证签名
if (biwConfig.getEnable() && !sign.equals(signRequest)) {
log.error("签名验证失败, 签名计算值 {} 签名请求值{} body内容{}", sign, signRequest, bodyText);
this.doReturn(response, Result.createError(ResultCodeEnum.SIGN_ERROR.getCode(), ResultCodeEnum.SIGN_ERROR.getMessage()));
return;
}
filterChain.doFilter(requestWrapper, response);
} catch (Exception e) {
log.error("签名验证异常", e);
this.doReturn(response, Result.create500Error(e.getMessage()));
return;
}
}
/**
* readHttpBody
*
* @param requestWrapper
* @return
* @throws IOException
*/
private String readHttpBody(ServletRequest requestWrapper) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(requestWrapper.getInputStream(), Charset.forName("UTF-8")));
String line = "";
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
/**
* doReturn
*
* @param response
* @param result
* @throws IOException
*/
private void doReturn(HttpServletResponse response, Result result) throws IOException {
ServletOutputStream out = response.getOutputStream();
out.write(JSON.toJSONString(result).getBytes());
out.flush();
}
/**
* signMD5 : MD5签名加密
*
* @param jsonObject
* @return
*/
public String signMD5(JSONObject jsonObject) {
Iterator it = jsonObject.getInnerMap().keySet().iterator();
Map<String, Object> map = new TreeMap<String, Object>();
StringBuilder signSb = new StringBuilder();
while (it.hasNext()) {
Object key = it.next();
Object value = jsonObject.get(key);
if (SIGN_FIELD_NAME.equals(key)) {
continue;
}
map.put(key.toString(), value);
}
for (Map.Entry<String, Object> entry : map.entrySet()) {
signSb.append(entry.getKey());
signSb.append("=");
signSb.append(entry.getValue());
signSb.append("&");
}
signSb.append(KEY_FIELD_NAME).append("=").append(biwConfig.getSecurityKey());
String sign = DigestUtils.md5Hex(signSb.toString()).toUpperCase();
return sign;
}
/**
* 生成签名
*/
public static void main(String[] args) {
String json = "{\"endTime\":\"2023-07-01 08:00:00\",\"startTime\":\"2023-07-01 00:00:00\",\"pageNum\":1,\"pageSize\":50,\"requestId\":\"test001\"}";
JSONObject jsonObject = JSON.parseObject(json);
Iterator it = jsonObject.getInnerMap().keySet().iterator();
Map<String, Object> map = new TreeMap<String, Object>();
StringBuilder signSb = new StringBuilder();
while (it.hasNext()) {
Object key = it.next();
Object value = jsonObject.get(key);
if ("sign".equals(key)) {
continue;
}
map.put(key.toString(), value);
}
for (Map.Entry<String, Object> entry : map.entrySet()) {
signSb.append(entry.getKey());
signSb.append("=");
signSb.append(entry.getValue());
signSb.append("&");
}
signSb.append("key").append("=").append("ccf12f15155c9c564daf1783a6f65f69a4a0");
String sign = DigestUtils.md5Hex(signSb.toString()).toUpperCase();
System.out.println(sign);
}
}
4、注册自定义过滤器;
import com.test.baidu.config.BiwConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author
* @date 2023/07/18
* @description: 系统过滤配置
*/
@Configuration
public class BiwFilterConfig {
@Autowired
private BiwSignFilter biwSignFilter;
@Autowired
private BiwConfig biwConfig;
/**
* biwBillPullFilterConfig
* 数据-签名过滤
*
* @return
*/
@Bean
public FilterRegistrationBean<BiwSignFilter> biwBillPullFilterConfig() {
FilterRegistrationBean<BiwSignFilter> registration = new FilterRegistrationBean<>();
// 注册自定义过滤器
registration.setFilter(biwSignFilter);
// 过滤所有路径
// registration.addUrlPatterns(biwConfig.getUrlPathPatterns().split(","));
registration.addUrlPatterns(biwConfig.getUrlPathPatterns());
// 过滤器名称
registration.setName("biwParametersFilter");
// 优先级,越低越优先
registration.setOrder(1);
return registration;
}
}
5、每次调用此方法时将数据流中的数据读取出来,然后再回填到InputStream之中
解决通过@RequestBody和@RequestParam(POST方式)读取一次后控制器拿不到参数问题
import org.apache.commons.io.IOUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* @Author
* @create 2023/07/18
*/
public class RequestWrapper extends HttpServletRequestWrapper {
private byte[] requestBody;
private HttpServletRequest request;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.request = request;
}
@Override
public ServletInputStream getInputStream() throws IOException {
/**
* 每次调用此方法时将数据流中的数据读取出来,然后再回填到InputStream之中
* 解决通过@RequestBody和@RequestParam(POST方式)读取一次后控制器拿不到参数问题
*/
if (null == this.requestBody) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(request.getInputStream(), baos);
this.requestBody = baos.toByteArray();
}
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() {
return bais.read();
}
};
}
public byte[] getRequestBody() {
return requestBody;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
本文介绍了如何在SpringBoot项目中使用Java实现一个接口验证过滤器,包括配置文件管理、自定义OncePerRequestFilter的扩展,以及对HTTP请求体的签名验证过程。
1万+

被折叠的 条评论
为什么被折叠?



