`
TableMiao
  • 浏览: 75437 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

拦截器、过滤器、参数读取坑记录

阅读更多

拦截器、过滤器、参数读取坑记录

 

  目的:做个统一网关接口、app请求要跳到H5的接口复用

 

主要问题:

   1.H5的登录信息获取 是通过Session 来获取 ,app则是mid(用户id)来获取,信息来源不同

   2.如何区分终端来源是H5还是app

 

解决思路一:

写一个公用方法,在每一个controller中去调用,判断参数,如果是app的则通过mid获取,H5走session

 

解决思路二:

   拦截器完成,在preHandle 中统一拦截配置中需要跳转到H5的请求url,统一取登录信息,放入到缓存或者request中,controller只需要从request中或者缓存中获取登录信息不需要再去判断来源

 

 

解决如下:
    不管是思路一还是思路二,都会遇到同一个问题,如何取区分app和H5的终端参数,如何取?

1.公用参数里有system(ios、Android)通过这个来区分

 

2.由于app所有的请求都是post且参数都在body里面,请求头类型为application/json

用request.getParameter(xxx)只能拿到url后面以?xxx=xxx这种拼接格式   或者 请求头类型为 application/x-www-form-urlencoded  这种的。 所以只能用流来解析app传过来的参数。 现有的controller为什么不是流解析,是因为 我们controller里有@RequestBody 来标识绑定body参数,所以我们可以用标识后面的map或者是对象来取。

 

3.流解析弊端,通俗点就是只能解析一遍,也就是说拦截器中解析一遍拿到systeme参数判断是app还是H5之后,后续controller就没法取到参数值了。原因是因为流解析之后是要关闭的,无法再往下传。所以引入filter 把我们的request重写之后再往下传。

 

 

代码:

1.拦截器 CheckCmbMemberInterceptor  主要拦截配置中需要跳转的url,不同的终端不同获取登录对象,统一放到request中。

 

2.过滤器RequestFilter 主要保存request 信息 用于后续controller操作。后续如果有敏感词过滤,黑白名单,接口开关控制等等, 都可以通过这个filter来实现!

 

拦截器代码

    springmvc的xml

	<mvc:interceptors>
		<mvc:interceptor>
			<mvc:mapping path="/**" />
			<mvc:exclude-mapping path="/error/*.json" />
			<bean
				class="com.xxx.xxx.xxx.interceptor.CheckCmbMemberInterceptor">
				<property name="excludeUrlPattern">
					<list>
						<value>/user/myWallet</value>
					</list>
				</property>
			</bean>
		</mvc:interceptor>
	</mvc:interceptors>

 

对应的拦截器

CheckCmbMemberInterceptor
package com.xxx.xxx.web.interceptor;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.dubbo.common.utils.IOUtils;
import com.google.gson.Gson;
import com.xxx.xxx.model.CmbMember;
import com.xxx.xxx.service.core.consumer.QrpayMemberService;
import com.xxx.xxx.utils.BaseResult;
import com.xxx.xxx.utils.ErrorCodeEnum;
import com.xxx.xxx.utils.LogPrintUtil;
import com.xxx.xxx.web.interceptor.req.BaseReq;
import com.xxx.xxx.web.interceptor.req.CustomReq;

/**
 * 登入检查的拦截器。 区分 CmbMember 来源 是H5还是app
 * 
 */
public class CheckCmbMemberInterceptor implements HandlerInterceptor {

	private List<String> excludeUrlPattern;

	private AntPathMatcher urlPatternMatcher = new AntPathMatcher();

	private Gson gson = new Gson();

	private static final String SYSTEM_IOS = "ios";
	private static final String SYSTEM_ANDROID = "android";

	@Autowired
	private QrpayMemberService qrpayMemberService;

	// @Autowired
	// private JedisApi jedisApi;

	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		// 如果没在配置中,直接返回
		for (int i = 0; excludeUrlPattern != null && i < excludeUrlPattern.size(); i++) {
			if (!urlPatternMatcher.match(excludeUrlPattern.get(i), request.getServletPath())) {
				return true;
			}
		}

		// 获取参数判断终端返回
		BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
		String reqBody = IOUtils.read(reader);
		BaseReq req = gson.fromJson(reqBody, BaseReq.class);
		LogPrintUtil.printFreeLog("reqId", "CheckCmbMemberInterceptor", "preHandle", "统一拦截器公共req参数", req);

		if (req != null
				&& (SYSTEM_IOS.equalsIgnoreCase(req.getSystem()) || SYSTEM_ANDROID.equalsIgnoreCase(req.getSystem()))) {
			return appHandle(request, response, handler, reqBody);

		} else {

			return h5Handle(request, response, handler);
		}

	}

	private boolean appHandle(HttpServletRequest request, HttpServletResponse response, Object handler, String reqBody)
			throws Exception {

		// 获取用户信息,放入
		CustomReq req = gson.fromJson(reqBody, CustomReq.class);

		if (req.getMid() == null) {
			writeError(request, response, handler, BaseResult.buildFailBaseResult(ErrorCodeEnum.ERROR_402.getCode()));
			return false;
		}

		CmbMember cmbMember = qrpayMemberService.getMemberByMid(Long.valueOf(req.getMid()));
		if (cmbMember == null) {
			writeError(request, response, handler, BaseResult.buildFailBaseResult(ErrorCodeEnum.ERROR_450.getCode()));
			return false;
		}

		request.setAttribute("member", cmbMember);

		return true;

	}

	private void writeError(HttpServletRequest request, HttpServletResponse response, Object handler,
			@SuppressWarnings("rawtypes") BaseResult br) throws Exception {

		OutputStream output = null;
		response.setContentType("application/json");
		response.setCharacterEncoding("UTF-8");
		output = response.getOutputStream();
		byte[] rspJson = new Gson().toJson(br, br.getClass()).getBytes("UTF-8");
		output.write(rspJson);
		output.flush();

	}

	private boolean h5Handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		if (request.getSession() == null) {
			// 由于H5返回的格式各异,不做统一拦截保持原有逻辑 在controller中作返回错误处理
			LogPrintUtil.printFreeLog("reqId", "CheckCmbMemberInterceptor", "H5Handle", "统一拦截器H5",
					"request.getSession()为空");
			return true;
		}
		Object member = request.getSession().getAttribute("member");
		request.setAttribute("member", member);
		return true;
	}

	public void setExcludeUrlPattern(List<String> excludeUrlPattern) {
		this.excludeUrlPattern = excludeUrlPattern;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
	}
}

 接口controller 调用

	@RequestMapping(value = "/myWallet")
	public String myWallet(HttpServletRequest request) throws Exception {
		//查询用户资金信息
		//Object member = request.getSession().getAttribute("member");
		Object member = request.getAttribute("member");
		if(member == null){
			logger.info("当前操作用户session失效,直接跳转登录页面");
			return "/user/login";
		}
		CmbMember cmbMember = (CmbMember)member;
		CmbFundAct cmbFundAct = this.qrpayMemberCommonService.getMemberFund(cmbMember.getMid());
		request.setAttribute("cmbFundAct",cmbFundAct);
		return "user/myWallet";
	}

 

filter代码

      web.xml声明

	<!-- 过滤request请求的Filter -->
	<filter>
		<filter-name>requestFilter</filter-name>
		<filter-class>com.xxx.xxx.web.filter.RequestFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>requestFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 对应的

RequestFilter
package com.xxx.xxx.web.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import com.xxx.xxx.web.interceptor.util.BodyReaderHttpServletRequestWrapper;

/**
 * 过滤器把请求流保存起来 接着传递到controller
 *
 */
public class RequestFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
		// 防止流读取一次后就没有了, 所以需要将流继续写出去
		ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
		chain.doFilter(requestWrapper, response);
	}

	@Override
	public void destroy() {

	}

}

 过滤器中重写的

BodyReaderHttpServletRequestWrappe

 

package com.xxx.xxx.web.interceptor.util;


import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import com.alibaba.dubbo.common.utils.IOUtils;

/***
 * 构造一个 BodyReaderHttpServletRequestWrapper 
 * 重写 HttpServletRequest 
 * 解决 IO读取body参数,后续request为空的情况
 * 
 */
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), Charset.forName("UTF-8")));
        String reqBody = IOUtils.read(reader);
        body = reqBody.getBytes(Charset.forName("UTF-8"));
    }

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

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }

			@Override
			public boolean isFinished() {
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public boolean isReady() {
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public void setReadListener(ReadListener readListener) {
				// TODO Auto-generated method stub
				
			}
        };
    }

    @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);
    }
}

 

分享到:
评论

相关推荐

    spring mvc拦截器过滤json中的null值

    我们可以利用拦截器来实现对HTTP请求的过滤,包括修改请求参数、日志记录、权限验证等。 要创建一个自定义的拦截器,你需要继承`HandlerInterceptorAdapter`类,并重写其中的三个方法: 1. `preHandle...

    Android-OkLogOkHttp的响应记录拦截器

    注意,由于日志拦截器可能会在多个请求之间共享,因此在生产环境中,为了性能考虑,我们应该避免在拦截器中直接读取响应体,而是应该只记录基本信息,例如状态码和头部信息。 要在OkHttp的Client中添加这个拦截器,...

    Android-OkHttp的请求重试拦截器

    1. **记录失败请求**:拦截器需要有能力存储失败的请求,包括请求方法(GET、POST等)、URL、请求头和请求体等信息。 2. **重试策略**:定义何时重试请求。通常,我们可以基于HTTP状态码、网络错误类型(如连接超时...

    struts拦截器介绍

    27. **Parameter FilterInterceptor**:参数过滤拦截器,从参数列表中删除不必要的参数。 28. **ProfilingInterceptor**:性能剖析拦截器,可以通过参数激活性能分析,默认的拦截器栈设计适用于大多数应用程序。 ...

    Java Struts 实现拦截器

    2. **标准过滤器链**: 请求被一系列的标准过滤器处理,这些过滤器包括但不限于`ActionContextCleanUp`(清理当前线程的ActionContext等)和`FilterDispatcher`(通过`ActionMapper`来确定需要调用哪个Action)。...

    ssh最新框架搭建,加入拦截器

    在Struts2中,拦截器是实现业务逻辑增强的一种方式,它们在Action执行前后执行,可以实现日志记录、权限验证、性能监控等功能。要添加拦截器,你需要在struts.xml配置文件中定义拦截器栈,并关联到特定的Action或者...

    电商数仓项目(八) Flume(2) 拦截器开发源代码

    Flume 拦截器允许我们在数据流入通道之前对其进行预处理,比如过滤、修改或添加元数据等操作。了解如何开发拦截器对于优化数据流和满足特定业务需求非常有用。 首先,我们需要理解 Java 在 Flume 中的角色。Java 是...

    struts2拦截器一些用法

    拦截器可以看作是一种特殊的过滤器(Filter),但与传统的Servlet过滤器相比,它更加轻量级,并且能够更好地与Struts2框架集成。 #### 二、拦截器的工作原理 1. **加载配置**:当应用程序启动时,Struts2会读取...

    Android 短信拦截器

    9. **安全考虑**: 短信拦截器有潜在的安全风险,因为它可以读取所有短信,包括验证码和敏感信息。因此,只有在充分理解其工作原理和风险后,才能在生产环境中使用。 10. **测试与调试**: 在实际设备上进行测试非常...

    JAVA过滤器标准代码

    在Java Web开发中,过滤器(Filter)是一种用于拦截请求和响应的重要机制,可以实现对用户请求的预处理和后处理,以及对响应的处理。本文将深入解析“JAVA过滤器标准代码”,探讨其核心功能、实现原理以及应用场景。...

    Struts2,实现单个文件,多个文件,上传与下载,多个拦截器

    拦截器是Struts2框架中的一个重要组件,用于在执行Action前后进行一系列处理,如验证用户输入、日志记录、事务管理等。在struts.xml文件中,可以定义全局或特定Action的拦截器栈,通过`&lt;interceptors&gt;`和`...

    可以通过解析器把要解析的文件和拦截器关联起来进行

    4. **处理结果**:解析完成后,拦截器可以对结果进行进一步处理,如校验、过滤或转换,确保数据的质量和安全性。 这种结合使用的方式具有以下优势: - **可扩展性**:通过添加或移除拦截器,可以轻松地扩展解析器...

    Struts 2 过滤器的总结

    - `init(FilterConfig config)`:初始化过滤器,读取配置参数。 - `doFilter(ServletRequest request, ServletResponse response, FilterChain chain)`:核心逻辑,执行过滤操作。 - `destroy()`:释放资源,清理...

    servlet过滤器详解

    1. **初始化阶段**:当Web应用启动时,容器会读取`web.xml`文件中的配置信息来创建并初始化过滤器对象。 2. **请求处理阶段**:每当一个请求到达时,如果该请求匹配了某个过滤器,则该过滤器的`doFilter()`方法将被...

    拦截器原理及案例(csdn)————程序.pdf

    与过滤器相比,拦截器提供了更细粒度的控制,可以在多个阶段进行干预。 ### 1. 概念理解 - **执行顺序图**:拦截器的执行顺序通常表现为一个链式调用的过程,preHandler()方法从上到下依次执行,postHandler()和...

    java 消息过滤器的实现

    2. **过滤器配置**:在Web应用的`web.xml`配置文件中,可以定义过滤器的类名、过滤器的映射路径以及过滤器的初始化参数。 3. **过滤器生命周期**:包括`init()`、`doFilter()`和`destroy()`方法,`init()`在过滤器...

    struts2用cookie实现自动登录中用过滤器执行读取上下文方法

    这个过程涉及到几个关键步骤,包括登录处理、Cookie的创建与设置、过滤器的配置以及在请求处理之前读取Cookie中的信息来恢复上下文。 首先,当用户成功登录后,服务器端会创建一个包含用户身份信息的Cookie。这个...

    用过滤器验证

    过滤器可以拦截用户请求,并对请求进行预处理(如设置编码、登录验证等),也可以在服务器响应后进行后处理(如压缩响应数据、添加额外的头部信息等)。通过使用过滤器,可以简化应用开发,并将一些通用功能(如登录...

Global site tag (gtag.js) - Google Analytics