Filter 处理 request 和 response

本文介绍了如何在Java的Filter中处理请求和响应,特别是在登录验证和缓存策略上的应用。针对session管理和PC及移动终端的登录逻辑进行了详细说明。在缓存部分,提出根据请求参数和方法缓存数据以减少数据库查询。同时,解决了请求流的读取问题,通过包装HttpServletRequest和HttpServletResponse来避免重复读流。在响应数据写回时,注意设置响应头以确保客户端能接收到数据。最后,对于前端Base64编码的数据,采用特定的解码方法解决了解码不完整的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Filter 处理 request 和 response

背景:在 Filter 根据请求参数,判断用户登陆是否有效。后来是想就请求参数和返回值之间的关系做一个缓存。至于为什么不用 Spring … 因为公司不用。所以项目都是 Servlet + Filter …

  • session:
    关于这个,有必要提一下:所谓的 session 失效是在一定时间内没有 session 活动,然后 session 失效。浏览器的 Cookie 中 JSESSIONID 就是保存当前会话的 sessionId, 每一次浏览器请求服务器时,服务器根据 JSESSIONID 中的值来查找当前有效的 session,若存在则把上下文切换到该 session,否则创建一个新的 session 并生产一个新的 sessionId。

  • 登陆 PC
    PC 成功登陆的时候记录当前的账户 Id 和 sessionId,sessionId 为 key;并会根据账户 Id 清除此前已经记录的 sessionId 。在 Filter 中根据当前的 sessionId 去查找记录的账户 Id,如果找不到或者找到的与当前发起请求的 账户 Id 不匹配,则判断用户需要登陆。(虽然有点儿 low 但貌似也保证了只能在一个浏览器登陆)

  • 移动终端
    思路具是一样的,在成功登陆之后会把当前的 sessionId 返回给移动端。然后每次请求的时候把 获取到的 sessionId 加入 header 中,并把获取到的 sessionId 加入传入参数中(个人觉得没必要加入参数中,服务端可以通过读取 cookie 来获取移动端传入的 sessionId)。然后通过比较 参数中的 sessionId 和 服务器中的 sessionId 来判断是否登陆超时。

  • 缓存
    这个预想是根据 request 中的参数和请求的方法,来缓存一部分数据。这样就没必要每次去查数据库了。

问题

  • 毫无疑问是流,因为流在被读了一次之后第二次就不能读了
  1. request:
    需要包装一下:下面的类,把流信息读取后存入 byte 数组,然后重写了 getInputStream 方法,当获取流的时候就读取 byte 数组的数据。

    		// 略
    BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(hrequest);
    // 略
    

    然后通过 chain.doFilter(requestWrapper, response); 调用就不会存在重复读流的问题。

    // BodyReaderHttpServletRequestWrapper.java
    public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper{
    private byte[] body;
    
     public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
         super(request);
    
         InputStream is = request.getInputStream();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         byte buff[] = new byte[ 1024 ];
         int read;
         while( ( read = is.read( buff ) ) > 0 ) {
             baos.write( buff, 0, read );
         }
         this.body = baos.toByteArray();
     }
    
    public BufferedReader getReader() throws IOException {
         return new BufferedReader(new InputStreamReader(getInputStream()));
     }
    
    @Override
     public ServletInputStream getInputStream() throws IOException {
         return new BufferedServletInputStream(this.body);
     }
    }
    
    

    BodyReaderHttpServletRequestWrapper 这个代码是网上找的。

  2. response
    也是需要包装一下:因为如果在 chain.doFilter(requestWrapper, responseWrapper); 之后读取了 response 中的数据进行缓存,那客户端就不能读取数据。 所以和 request 一样要缓存,其实这里就是把数据读出来,然后再处理后再写会流中。参考代码如下:

       public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
           throws IOException, ServletException {
         HttpServletRequest req = (HttpServletRequest) request;
         HttpServletResponse resp = (HttpServletResponse) response;
    
         String reqUrl = req.getServletPath().trim();
         if (reqUrl.indexOf("doAction") != -1 || reqUrl.indexOf("doPost") != -1) {
           BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(req);
    
           // 包装响应对象  resp     并缓存响应数据
           ResponseWrapper responseWrapper = new ResponseWrapper(resp);
           chain.doFilter(requestWrapper, responseWrapper);
    
           byte[] bytes = responseWrapper.getBytes(); // 获取缓存的响应数据
    
           ByteArrayOutputStream bout = new ByteArrayOutputStream();
           GZIPOutputStream gzipOut = new GZIPOutputStream(bout); // 创建
           gzipOut.write(bytes); // 将响应的数据写到 Gzip 压缩流中
           gzipOut.close(); // 将数据刷新到 bout 字节流数组
           byte[] bts = bout.toByteArray();
           resp.setHeader("Content-Encoding", "gzip"); // 设置响应头信息
           resp.getOutputStream().write(bts); // 将压缩数据响应给客户端
    
         } else {
           chain.doFilter(request, response);
         }
       }
    

    惭愧,这段代码也是参考别人的。时间太久不记得了……

  3. 写数据的问题
    有时候需要提前返回结果,就需要往 response 中写一定的数据给客户端做响应。这里遇到的问题是响应头设置不对,客户端拿不到数据。最后的代码如下:

       	public static void writeJSONToClientJson(ServletResponse response, String state, String msg) {
           try {
    
             HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper((HttpServletResponse) response);
             JSONObject obj = new JSONObject();
             obj.put("STATE", state);
             obj.put("MSG", msg);
             responseWrapper.setCharacterEncoding("UTF-8");
             responseWrapper.setContentType("text/html;charset=utf-8");
             PrintWriter out = responseWrapper.getWriter();
             out.print(obj.toString());
             out.flush();
             out.close();
           } catch (Exception e) {
             Log.log(e);
           }
         }
    
    
  4. Base64 解码问题
    前端传回来的数据是 Base64 编码的,解码的时候就发现使用在线解码工具是能正常解码的,但是用 JDK 的解码出来就数据就不全。最后 使用的是 org.apache.axiom.util.base64.Base64Utils.decode(String) 这个方法。 这个很伤,试了好多都不能正常解码出来……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值