利用Filter和HttpServletRequestWrapper实现请求体中token校验

  先说一下项目的背景,系统传参为json格式,token为其中一个必传参数,此时如果在过滤器中直接读取request,则后续controller中通过RequestBody注解封装请求参数是会报stream closed异常,一位InputStream是一个基础流,只能被读取一次。代码如下:

package com.hellobike.scm.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.hellobike.basic.model.sys.ScmUser;
import com.hellobike.basic.util.Utils;
import com.hellobike.scm.conf.UserThread;
import com.hellobike.scm.service.RedisCacheService;
import com.hellobike.scm.service.RedisService;
import com.hellobike.scm.util.LoginUserUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

public class AuthFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);


    @Autowired
    private RedisCacheService redisCacheService;


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext());
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        String method = httpRequest.getMethod();
        httpResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpResponse.setContentType("application/json; charset=utf-8");
        httpResponse.setHeader("pragma", "no-cache");
        httpResponse.setHeader("cache-control", "no-cache");
        LoginUserUtil.removeCurrentUserInfo();

        String requestUrl = httpRequest.getRequestURI();
        //|| requestUrl.contains("/systemConfig/getUpgradeInfo")
        if (requestUrl.contains(".css") || requestUrl.contains(".js") || requestUrl.contains(".png")
                || requestUrl.contains(".jpg") || requestUrl.contains("/execption/")||requestUrl.contains("/sys/main/login") ||requestUrl.contains("/sys/main/checkCode")) {
            // 如果发现是css或者js文件,直接放行
            filterChain.doFilter(request, response);
            return;
        }
        String userAgent = httpRequest.getHeader("User-Agent");
        String reqCxtType = httpRequest.getContentType();
        boolean isJsonType = shouldLog(reqCxtType);
        String token = null;
        Map<String, String[]> param = request.getParameterMap();
        if (param.containsKey("token")) {
            token = param.get("token")[0];
            logger.info("authFilter中get请求token值为{}"+token);
        }
        if (token == null) {
            String body = null;
            // 再从contentType取token
            if (isJsonType) {
                httpRequest = new RequestWrapper((HttpServletRequest) request);
                body = ((RequestWrapper) httpRequest).getBody();
                JSONObject params = JSON.parseObject(body);
                logger.info("post param is {}", body);
                if (params == null) {
                    request.getRequestDispatcher("/admin/execption/tokenError").forward(request, response);
                    return;
                }
                token = params.getString("token");
            }
        }
        // 最后从form_data里面取token
        if (null == token) {
            if (request.getContentType() != null && request.getContentType().contains("multipart/form")) {
                MultipartHttpServletRequest multiReq = null;
                try {
                    multiReq = new CommonsMultipartResolver().resolveMultipart(httpRequest);
                    token = multiReq.getParameter("token");
                    logger.info("multiReq token=" + token);
                    // request = multiReq;
                } catch (Exception e) {
                    logger.info("MultipartHttpServletRequest异常");
                    e.printStackTrace();
                }
            }
        } 
        if (isJsonType && !tokenAvailable(token, userAgent)) {
            request.getRequestDispatcher("/admin/execption/tokenError").forward(request, response);
            return;
        } 
        if (!tokenAvailable(token, userAgent)) {
            request.getRequestDispatcher("/admin/execption/tokenError").forward(request, response);
            return;
        }
        // url权限校验
           ScmUser adminUser = JSON.parseObject(redisCacheService.getToken(token),ScmUser.class);
           UserThread.setValue(adminUser);
            String userName = null;
            if (adminUser != null) {
                userName=adminUser.getUserName();
                LoginUserUtil.setCurrentUserName(userName);
            }
            //判断是否系统升级
            if(upgrade(userName,requestUrl)){
                request.getRequestDispatcher("/systemConfig/getUpgradeInfo").forward(httpRequest, response);
                return;
            }

            logger.info("data的值{}", userName);
            String permiCode = (String) redisCacheService.getUserPermission(userName, requestUrl);
            logger.info("requestUrl={},permiCode={}", requestUrl, permiCode);
            if (permiCode != null && "None".equals(permiCode)) {
                // returnTokenError(httpResponse,ErrorCode.UNAUTHORIZED);
                request.getRequestDispatcher("/admin/execption/unauthorized").forward(request, response);
                return;
            }
        filterChain.doFilter(httpRequest, httpResponse);
    }

    private boolean upgrade(String userName,String requestUrl){
        Set<String> userNames = redisCacheService.get("scm_upgrade_tag" , Set.class);
        if (userNames != null&& (!requestUrl.equals("/systemConfig/getUpgradeInfo")) &&(!userNames.contains(userName))) {
            return true;
        }
        return false;
    }

    private boolean tokenAvailable(String token, String userAgent) {
        if (Strings.isNullOrEmpty(token)) {
            return false;
        }
        try {
            String data = redisCacheService.getToken(token);
            if (Utils.isEmpty(data)) {
                return false;
            }
            redisCacheService.set(token, data, 1200);
        } catch (Exception e) {
            logger.info("校验token失败");
        }
        return true;
    }

    @Override
    public void destroy() {

    }

    private boolean shouldLog(String contentType) {
        String jsonType = "application/json";

        if (contentType == null) {
            return false;
        }
        String[] cells = contentType.split(";");
        for (String cell : cells) {
            if (cell.trim().toLowerCase().equals(jsonType)) {
                return true;
            }
        }

        return false;
    }
}
package com.hellobike.manage.flter;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

public class RequestWrapper extends HttpServletRequestWrapper {
    private final String body;
    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }
        body = stringBuilder.toString();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new     ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    public String getBody() {
        return this.body;
    }

    private Map<String , String[]> params = new HashMap<String, String[]>();


    public void setParameter(String name, Object value) {
        if (value != null) {
            System.out.println(value);
            if (value instanceof String[]) {
                params.put(name, (String[]) value);
            } else if (value instanceof String) {
                params.put(name, new String[]{(String) value});
            } else {
                params.put(name, new String[]{String.valueOf(value)});
            }
        }
    }

    @Override
    public String getParameter(String name) {
        String[]values = params.get(name);
        if(values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }

}

  下面说一下我遇到的坑,系统要做一个简单的系统升级界面,此时用户不可访问,因此做了个请求转发,在springboot1.5.1版本中,FilterRegistrationBean默认会拦截转发的请求,此时request.getRequestDispatcher("/systemConfig/getUpgradeInfo").forward(request, response)会被再次拦截,因为request中流已被读取,所以此时RequestWrapper会报stream closed。在springboot2.0.4版本中,FilterRegistrationBean默认不会拦截转发(FORWARD)类型的请求,所以不会报stream closed,所以第一个坑就是FilterRegistrationBean默认的拦截类型的设置。修改转发方法传参为httpRequest,即使用RequestWrapper封装后的httpRquest,转发方式变为request.getRequestDispatcher("/systemConfig/getUpgradeInfo").forward(httpRquest, response),此时在配置FilterRegistrationBean拦截转发(FORWARD)类型的请求后,会出现无限循环的现象,一直转发下去,这也是第二个坑,因此我们要在转发是否的判断条件是将我们要转发的URL排除在外,即

requestUrl.equals("/systemConfig/getUpgradeInfo")不再进行转发。

  问题虽然简单,但是前前后后还是浪费不少时间,特记录一下

转载于:https://www.cnblogs.com/hhhshct/p/11228854.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在SpringMVC中,如果需要重新设置请求体,可以使用HttpServletRequestWrapper来包装请求。首先,在自定义的Interceptor中,我们可以通过HttpServletRequest的getInputStream方法获取到请求体的内容。然而,一旦读取了请求体,后续的流程就无法再次读取了,导致请求处理失败。为了解决这个问题,我们可以使用HttpServletRequestWrapper类来对原始请求进行包装,以实现请求体的重复读取。 具体的代码如下所示: ```java public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper { private final String body; public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); body = getRequestBody(request); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes()); return new ServletInputStream() { @Override public int read() throws IOException { return byteArrayInputStream.read(); } }; } private String getRequestBody(HttpServletRequest request) throws IOException { // 从原始请求中获取请求体 BufferedReader reader = request.getReader(); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } return sb.toString(); } } ``` 在Interceptor的preHandle方法中,我们可以将原始的HttpServletRequest替换为自定义的HttpServletRequestWrapper,以实现请求体的重新设置。具体代码如下所示: ```java @Component @Slf4j public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 获取请求体的内容 String body = StringUtils.EMPTY; if (request instanceof MyHttpServletRequestWrapper) { body = ((MyHttpServletRequestWrapper) request).getBody(); } // 在这里可以对请求体进行处理或重新设置 // ... return true; } } ``` 通过这种方式,我们就可以在Interceptor中重新设置请求体,并且不会影响到后续的请求处理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [在Springboot HandlerInterceptor中获取GET和POST请求参数](https://blog.csdn.net/xindoo/article/details/127464604)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [浅谈SpringMVC HandlerInterceptor诡异问题排查](https://download.csdn.net/download/weixin_38659248/12749173)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值