通过HttpServletResponseWrapper修改response输出流
2024-10-19 15:31:14
在项目中遇到一个问题,需要对接口返回的数据进行加密给前端。项目中的controller一般都是返回一个实体form,重写的一个视图解析器继承ModelAndViewResolver,对返回的form转成json格式返回给前端。
视图解析器:
public class JsonModelAndViewResolver implements ModelAndViewResolver,
InitializingBean, ApplicationContextAware {
private String defaultContentType = "text/html";
private Log log = LogFactory.getLog(super.getClass());
private JsonSerialization jsonSerialization;
private ApplicationContext applicationContext; public void afterPropertiesSet() throws Exception {
if (this.jsonSerialization != null)
return;
this.jsonSerialization = JsonSerializationFactory.getInstance(this.applicationContext);
} public void setApplicationContext(ApplicationContext paramApplicationContext)
throws BeansException {
this.applicationContext = paramApplicationContext;
} //执行这个方法
public ModelAndView resolveModelAndView(Method paramMethod,
Class paramClass, Object paramObject,
ExtendedModelMap paramExtendedModelMap,
NativeWebRequest paramNativeWebRequest) {
if (Rest.class.isAssignableFrom(paramClass)) {
try {
HttpServletResponse localHttpServletResponse = (HttpServletResponse) paramNativeWebRequest.getNativeResponse(HttpServletResponse.class);
responseJson(paramObject, localHttpServletResponse);
} catch (IOException localIOException) {
throw new WebException(localIOException.getMessage(),
localIOException);
}
return null;
}
return UNRESOLVED;
} public void responseJson(Object paramObject,
HttpServletResponse paramHttpServletResponse) throws IOException {
if (!(StringUtils.hasText(paramHttpServletResponse.getContentType())))
paramHttpServletResponse.setContentType(this.defaultContentType);
String str = writeResult(paramObject);
PrintWriter localPrintWriter = paramHttpServletResponse.getWriter();
if (this.log.isInfoEnabled())
this.log.info("Rest result=" + str);
if ("{}".equals(str)) {
this.log.info("image stream is not write ");
return;
}
localPrintWriter.write(str);
localPrintWriter.flush();
} protected String writeResult(Object paramObject) {
String str = null;
if (paramObject == null) {
str = "{}";
} else if ((paramObject instanceof Number)
|| (paramObject instanceof Boolean)) {
str = "{\"resultCode\":\"" + paramObject.toString() + "\"}";
} else if ((paramObject instanceof String)) {
String result = (String) paramObject;
str = result;
} else {
if (paramObject instanceof ModelAndView)
paramObject = ((ModelAndView) paramObject).getModel();
str = getJsonSerialization().toJSONString(paramObject);
}
return str;
} public String getDefaultContentType() {
return this.defaultContentType;
} public void setDefaultContentType(String paramString) {
this.defaultContentType = paramString;
} public JsonSerialization getJsonSerialization() {
return this.jsonSerialization;
} public void setJsonSerialization(JsonSerialization paramJsonSerialization) {
this.jsonSerialization = paramJsonSerialization;
}
}
本来考虑直接修改视图解析器,对返回json串加密,但发现项目中有些接口直接在controller中直接通过PrintWriter返回了参数,显然有这种方法是拦截不到的。
最后通过HttpServletResponseWrapper截取返回数据流加密重新输出给前端的方式。
代码参照如下:
ResponseWrapper:
package com.paic.egis.smts.toa.web.interceptor; import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper; import com.paic.egis.smts.common.util.LoggerUtil; public class ResponseWrapper extends HttpServletResponseWrapper {
private PrintWriter cachedWriter;
private CharArrayWriter bufferedWriter; public ResponseWrapper(HttpServletResponse response) throws IOException {
super(response);
bufferedWriter = new CharArrayWriter();
cachedWriter = new PrintWriter(bufferedWriter);
} public PrintWriter getWriter() throws IOException {
return cachedWriter;
} public String getResult() {
byte[] bytes = bufferedWriter.toString().getBytes();
try {
return new String(bytes, "UTF-8");
} catch (Exception e) {
LoggerUtil.logError(this.getClass().getName(), "getResult", e);
return "";
}
} }
过滤器如下:
package com.paic.egis.smts.toa.web.filter; import java.io.IOException;
import java.io.PrintWriter; 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.HttpServletResponse; import com.alibaba.dubbo.common.utils.StringUtils;
import com.paic.egis.smts.common.util.LoggerUtil;
import com.paic.egis.smts.common.util.PropertiesUtil;
import com.paic.egis.smts.pama.security.Base64Utils;
import com.paic.egis.smts.toa.web.interceptor.ResponseWrapper;
import com.paic.egis.smts.trusteesship.util.RSACoder; public class ResponseWrapperFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException { String version = req.getParameter("version");
if(StringUtils.isEmpty(version)){ chain.doFilter(req, resp); } else { HttpServletResponse response = (HttpServletResponse) resp;
ResponseWrapper mr = new ResponseWrapper(response); chain.doFilter(req, mr); PrintWriter out = resp.getWriter();
try {
//取返回的json串
String result = mr.getResult();
System.out.println(result);
//加密
String encryptStr = encryptRSA(result);
out.write(encryptStr);
} catch (Exception e) {
LoggerUtil.logError(this.getClass().getName(), "doFilter", e);
} finally {
out.flush();
out.close();
}
}
} @Override
public void init(FilterConfig filterconfig) throws ServletException { } //rsa公钥加密
public String encryptRSA(String content) throws Exception{
String publicKeyStr = PropertiesUtil.getProperty("response.publicKey");
byte[] encryptBytes = RSACoder.encrypt(content.getBytes("utf-8"), publicKeyStr,"public");
return Base64Utils.encode(encryptBytes);
}
}
在测试阶段发现,有的接口会出现重复加密的问题。
过滤器配置如下:
<filter>
<filter-name>encryptFilter</filter-name>
<filter-class>com.paic.egis.smts.toa.web.filter.ResponseWrapperFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encryptFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>encryptFilter</filter-name>
<url-pattern>/do/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>encryptFilter</filter-name>
<url-pattern>/doh/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>encryptFilter</filter-name>
<url-pattern>*.doh</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>encryptFilter</filter-name>
<url-pattern>/app/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>encryptFilter</filter-name>
<url-pattern>/mamc/*</url-pattern>
</filter-mapping>
当接口地址是类似/do/smi/queryRegionInfo.do,过滤器类中会进入两次,对应同一个response,所以在第一次out.write(encryptStr); 时,就更改了response的输出值,在第二次String result = mr.getResult(); 时取得就是第一次加密后的值。
将接口改为/smi/queryRegionInfo.do就不会出现这种情况。
最新文章
- 【leetcode】Remove Nth Node From End of List
- querystring 解析url 查询字符串
- 系统补丁更新导致MVC3.0.0升级到3.0.1的问题解决
- Ubuntu deb包使用
- Linux搭建NFS提供磁盘给Windows使用
- MFC记住上次路径---遇到的一些问题
- C#生成Code39(extend)条形码【非条形码字体】
- 【小瑕疵】表单中的button会自动提交?
- 条件分支SQL语句<;一>; Case When
- C#实现基于ffmpeg加虹软的人脸识别
- Web上传文件的原理及实现
- MySQL升级方法一
- TFS 2017 培训 - 北京某银行科技部Java研发团队
- 【python3】爬取鼠绘汉化的海贼王漫画
- 关于css伪类
- ubuntu下安装低级版本gcc/g++ 并随意切换
- HiJson工具 &;&; 火狐浏览器中的jsonHandle插件(以及乱码问题的解决)-->;来转换json串的格式
- SQL Server ->;>; MSDB.DBO.AGENT_DATETIME函数从整型转时间日期格式
- dsu on tree总结
- 浏览器端js处理or直接冗余至服务器php处理?