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(); Mapmap = new TreeMap (); 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 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 map = new TreeMap (); 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 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 FilterRegistrationBeanbiwBillPullFilterConfig() { FilterRegistrationBean 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())); } }