在項目中遇到一個問題,需要對接口返回的數據進行加密給前端。項目中的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就不會出現這種情況。