屏蔽词过滤器 1:https://developer.aliyun.com/article/1379261
然后是过滤器中需要的过滤请求的封装好的request
package com.ruben.simplescaffold.filter.wrappers; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.ruben.simplescaffold.utils.sensitive.SensitiveWordUtils; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; /** * 封装请求 * * @author <achao1441470436@gmail.com> * @since 2021/8/7 21:50 */ public class RequestWrapper extends HttpServletRequestWrapper { private final Map<String, String[]> parameterMap; private final byte[] body; public RequestWrapper(HttpServletRequest request, SensitiveWordUtils sensitiveWordUtils, Map<String, String[]> parameterMap, String bodyString) { super(request); if (sensitiveWordUtils == null) { this.parameterMap = Optional.ofNullable(parameterMap).orElseGet(super::getParameterMap); queryStringPutParameterMap(super.getQueryString()); this.body = Optional.ofNullable(bodyString).orElseGet(() -> BodyHelper.getBodyString(request)).getBytes(StandardCharsets.UTF_8); } else { String paramString = JSON.toJSONString(Optional.ofNullable(parameterMap).orElseGet(super::getParameterMap)); paramString = sensitiveWordUtils.replaceSensitiveWord(paramString, SensitiveWordUtils.MAX_MATCH_TYPE, SensitiveWordUtils.REPLACE_CHAR); this.parameterMap = JSON.parseObject(paramString, new TypeReference<Map<String, String[]>>() { }); Optional.ofNullable(super.getQueryString()).ifPresent(queryString -> queryStringPutParameterMap(sensitiveWordUtils.replaceSensitiveWord(queryString, SensitiveWordUtils.MAX_MATCH_TYPE, SensitiveWordUtils.REPLACE_CHAR))); this.body = sensitiveWordUtils.replaceSensitiveWord(Optional.ofNullable(bodyString).orElseGet(() -> BodyHelper.getBodyString(request)), SensitiveWordUtils.MAX_MATCH_TYPE, SensitiveWordUtils.REPLACE_CHAR).getBytes(StandardCharsets.UTF_8); } } @Override public String getParameter(String name) { String[] values = parameterMap.get(name); if (Objects.isNull(values) || values.length == 0) { return null; } return values[0]; } @Override public Map<String, String[]> getParameterMap() { if (parameterMap == null) { return super.getParameterMap(); } return parameterMap; } @Override public Enumeration<String> getParameterNames() { if (parameterMap == null) { return super.getParameterNames(); } return new Vector<>(parameterMap.keySet()).elements(); } @Override public String[] getParameterValues(String name) { if (parameterMap == null) { return super.getParameterValues(name); } return parameterMap.get(name); } @Override public BufferedReader getReader() { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() { return byteArrayInputStream.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener arg0) { } }; } @Override public String getHeader(String name) { return super.getHeader(name); } @Override public Enumeration<String> getHeaderNames() { return super.getHeaderNames(); } @Override public Enumeration<String> getHeaders(String name) { return super.getHeaders(name); } private void queryStringPutParameterMap(String queryString) { if (queryString != null && queryString.trim().length() > 0) { String[] params = queryString.split("&"); for (String param : params) { int splitIndex = param.indexOf("="); if (splitIndex == -1) { continue; } String key = param.substring(0, splitIndex); if (!this.parameterMap.containsKey(key)) { if (splitIndex < param.length()) { String value = param.substring(splitIndex + 1); this.parameterMap.put(key, new String[]{value}); } } } } } public static class BodyHelper { /** * 获取请求Body * * @param request * @return */ public static String getBodyString(ServletRequest request) { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = request.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.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(); } } }
还有个获取响应结果的封装好的response
,虽然此处我们没用到,但如果我们要获取filter
中的响应体执行过滤,也可以用这个
package com.ruben.simplescaffold.filter.wrappers; import lombok.SneakyThrows; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; /** * 过滤器获取响应结果 * * @author <achao1441470436@gmail.com> * @since 2021/7/29 10:13 */ public class ResponseWrapper extends HttpServletResponseWrapper { private ByteArrayOutputStream buffer = null; private ServletOutputStream outputStream = null; private PrintWriter writer = null; /** * Constructs a response adaptor wrapping the given response. * * @param response The response to be wrapped * @throws IllegalArgumentException if the response is null */ @SneakyThrows public ResponseWrapper(HttpServletResponse response) { super(response); buffer = new ByteArrayOutputStream(); outputStream = new WrapperOutputStream(buffer); writer = new PrintWriter(new OutputStreamWriter(buffer, StandardCharsets.UTF_8)); } /** * The default behavior of this method is to return getOutputStream() on the * wrapped response object. */ @Override public ServletOutputStream getOutputStream() throws IOException { return outputStream; } /** * The default behavior of this method is to return getWriter() on the * wrapped response object. */ @Override public PrintWriter getWriter() throws IOException { return writer; } /** * The default behavior of this method is to call flushBuffer() on the * wrapped response object. */ @Override @SneakyThrows public void flushBuffer() { if (outputStream != null) { outputStream.flush(); } if (writer != null) { writer.flush(); } } /** * The default behavior of this method is to call reset() on the wrapped * response object. */ @Override public void reset() { buffer.reset(); } /** * Get response content * * @param charset HttpServletResponse#getCharacterEncoding() * @return response content */ @SneakyThrows public String getResponseData(String charset) { // 将out、writer中的数据强制输出到WrapperResponse的buffer里面,否则取不到数据 flushBuffer(); return buffer.toString(StandardCharsets.UTF_8.displayName()); } /** * 内部类,对ServletOutputStream进行包装,指定输出流的输出端 */ private static class WrapperOutputStream extends ServletOutputStream { private ByteArrayOutputStream bos = null; public WrapperOutputStream(ByteArrayOutputStream stream) throws IOException { bos = stream; } /** * Writes the specified byte to this output stream. The general * contract for <code>write</code> is that one byte is written * to the output stream. The byte to be written is the eight * low-order bits of the argument <code>b</code>. The 24 * high-order bits of <code>b</code> are ignored. * <p> * Subclasses of <code>OutputStream</code> must provide an * implementation for this method. * * @param b the <code>byte</code>. * @throws IOException if an I/O error occurs. In particular, * an <code>IOException</code> may be thrown if the * output stream has been closed. */ @Override public void write(int b) throws IOException { bos.write(b); } /** * Checks if a non-blocking write will succeed. If this returns * <code>false</code>, it will cause a callback to * {@link WriteListener#onWritePossible()} when the buffer has emptied. If * this method returns <code>false</code> no further data must be written * until the contain calls {@link WriteListener#onWritePossible()}. * * @return <code>true</code> if data can be written, else <code>false</code> * @since Servlet 3.1 */ @Override public boolean isReady() { return false; } /** * Sets the {@link WriteListener} for this {@link ServletOutputStream} and * thereby switches to non-blocking IO. It is only valid to switch to * non-blocking IO within async processing or HTTP upgrade processing. * * @param listener The non-blocking IO write listener * @throws IllegalStateException If this method is called if neither * async nor HTTP upgrade is in progress or * if the {@link WriteListener} has already * been set * @throws NullPointerException If listener is null * @since Servlet 3.1 */ @Override public void setWriteListener(WriteListener listener) { } } }
最后是测试用的controller
package com.ruben.simplescaffold.controller.rest; import com.ruben.simplescaffold.pojo.common.Result; import com.ruben.simplescaffold.pojo.dto.CommonDTO; import org.springframework.web.bind.annotation.*; /** * 测试控制层 * * @author <achao1441470436@gmail.com> * @since 2021/8/7 22:58 */ @RestController @RequestMapping("test") public class TestController { @PostMapping public Result testBody(@RequestBody CommonDTO commonDTO) { return Result.ok().data(commonDTO); } @GetMapping public Result testQueryParam(CommonDTO commonDTO) { return Result.ok().data(commonDTO); } @GetMapping("{path}") public Result testPath(@PathVariable String path) { return Result.ok().data(path); } }
这里还用到两个类,一个响应结果Result
,一个CommonDTO
,CommonDTO
大伙可以使用Map
代替下
package com.ruben.simplescaffold.pojo.common; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import org.springframework.http.HttpStatus; import java.util.HashMap; import java.util.Optional; /** * 自定义返回响应 * * @author <achao1441470436@gmail.com> * @since 2021/6/23 10:40 */ @Data @NoArgsConstructor @AllArgsConstructor @Accessors(fluent = true) @EqualsAndHashCode(callSuper = true) public class Result extends HashMap<String, Object> { public static final String DATA_KEY = "data"; private static final long serialVersionUID = 8639693790921600208L; /** * 成功状态 */ private Boolean success; /** * 消息 */ private String msg; /** * 状态码 */ private Integer code; public static Result ok() { return httpStatus(HttpStatus.OK); } public static Result error(String msg) { return httpStatus(HttpStatus.INTERNAL_SERVER_ERROR).msg(msg); } public static Result paramError() { return httpStatus(HttpStatus.BAD_REQUEST); } public static Result notLogin() { return httpStatus(HttpStatus.UNAUTHORIZED); } public static Result httpStatus(HttpStatus httpStatus) { httpStatus = Optional.ofNullable(httpStatus).orElse(HttpStatus.OK); HttpStatus.Series series = httpStatus.series(); return new Result().success(httpStatus.is2xxSuccessful()).msg(series.name()).code(HttpStatus.OK.value()); } /** * 放入key和value * * @param key 键 * @param value 值 * @return com.kuang.honghaisyweb.pojo.common.Result * @author <achao1441470436@gmail.com> * @since 2021/6/23 17:07 */ @Override public Result put(String key, Object value) { super.put(key, value); return this; } /** * 返回数据 * * @param value 数据 * @return com.kuang.honghaisyweb.pojo.common.Result * @author <achao1441470436@gmail.com> * @since 2021/6/23 17:07 */ public Result data(Object value) { this.put(DATA_KEY, value); return this; } }
最后是屏蔽词文本
屏蔽词1号 屏蔽词2号 屏蔽词3号
以及测试案例:
可以看到body
和param
传参都替换成了*
号,url
的path
传参也替换成了0