您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)spring boot如何實(shí)現(xiàn)在request里解密參數(shù)返回,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
有個(gè)業(yè)務(wù)需求,一個(gè)請(qǐng)求來(lái)源web,一個(gè)請(qǐng)求來(lái)源APP,web需求驗(yàn)證簽名,APP的參數(shù)是經(jīng)過(guò)加密,所以出現(xiàn)了兩個(gè)Controller,除了解密獲取參數(shù)方式不一樣,其他內(nèi)容一模一樣,這樣不太合理,所以我決定重構(gòu)。
思路:既然只是解密不一樣,獲取到的參數(shù)是一樣的,那可以寫(xiě)一個(gè)過(guò)濾器,在里面就把參數(shù)解密好,然后返回
Spring Boot在請(qǐng)求的時(shí)候是不允許直接修改HttpServletRequest里的paramsMap參數(shù)的,但是提供了一個(gè)HttpServletRequestWrapper類(lèi),繼承這個(gè)類(lèi)重寫(xiě)兩個(gè)方法就可以了。
重寫(xiě)HttpServletRequestWrapper
import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; public class ParameterRequest extends HttpServletRequestWrapper { private Map<String, String[]> params = new HashMap<>(16); public ParameterRequest(HttpServletRequest request) throws IOException { super(request); this.params.putAll(request.getParameterMap()); } /** * 重載一個(gè)構(gòu)造方法 * * @param request * @param extendParams */ public ParameterRequest(HttpServletRequest request, Map<String, String[]> extendParams) throws IOException { this(request); addAllParameters(extendParams); } @Override public String getParameter(String name) { String[] values = params.get(name); if (values == null || values.length == 0) { return null; } return values[0]; } @Override public String[] getParameterValues(String name) { return params.get(name); } public void addAllParameters(Map<String, String[]> otherParams) { for (Map.Entry<String, String[]> entry : otherParams.entrySet()) { addParameter(entry.getKey(), entry.getValue()); } } public void addParameter(String name, Object value) { if (value != null) { 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)}); } } } }
思路是重寫(xiě)自定義一個(gè)Map存入?yún)?shù),將解密后需要的參數(shù)放入,然后在過(guò)濾器中執(zhí)行這個(gè)新的request
過(guò)濾器
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.context.support.SpringBeanAutowiringSupport; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @Slf4j public class WebParamFilter implements Filter { private static final String OPTIONS = "OPTIONS"; @Value("${jwt.info.urlPatterns}") private List<String> urlPatterns; @Override public void init(FilterConfig filterConfig) throws ServletException { SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext()); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; log.info("開(kāi)始過(guò)濾器==============="); if (!isFilter(request)) { writerError(response, RetEnum.RET_TOKEN_ERROR); return; } // 從請(qǐng)求頭從獲取請(qǐng)求類(lèi)型,1是WEB,2是APP String requestType = request.getHeader("requestType"); if (StringUtils.isEmpty(requestType)) { writerError(response, RetEnum.RET_NOT_HEADER_ERROR); return; } Map<String, String[]> paramsMap = new HashMap<>(); if ("1".equals(requestType)) { // 驗(yàn)證簽名,簽名錯(cuò)誤直接返回 if (!compareSign(request)) { writerError(response, "簽名錯(cuò)誤", 500); return; } // 將請(qǐng)求的參數(shù)從request中取出,轉(zhuǎn)換成JSON,放入自定義的Map中帶給控制器 paramsMap.put("params", new String[]{JSONUtil.getJSONParam(request).toJSONString()}); ParameterRequest req = new ParameterRequest(request, paramsMap); filterChain.doFilter(req, response); } else if ("2".equals(requestType)) { // APP請(qǐng)求方式比較特殊,所以要從requestBody里讀出JSON加密數(shù)據(jù) String bodyStr = RequestBodyUtil.read(request.getReader()); // 然后再解密,拿到真正的參數(shù)轉(zhuǎn)換成JSON,放入自定義的Map中帶給控制器 JSONObject jsonParam = getJsonParam(bodyStr); paramsMap.put("params", new String[]{jsonParam.toJSONString()}); ParameterRequest req = new ParameterRequest(request, paramsMap); filterChain.doFilter(req, response); } else { writerError(response, "無(wú)效的請(qǐng)求來(lái)源", 500); } } @Override public void destroy() { } /** * 篩選 * * @param request * @return */ private boolean isFilter(HttpServletRequest request) { if (OPTIONS.equals(request.getMethod())) { return true; } if (isInclude(request)) { //如果是屬于排除的URL,比如登錄,注冊(cè),驗(yàn)證碼等URL,則直接通行 log.info("直接通過(guò)"); return true; } return tokenCheck(request); } /** * 排除不需要過(guò)濾的URL * * @param request * @return */ private boolean isInclude(HttpServletRequest request) { String url = request.getRequestURI().substring(request.getContextPath().length()); log.info("請(qǐng)求url:{}", url); for (String patternUrl : urlPatterns) { Pattern p = Pattern.compile(patternUrl); Matcher m = p.matcher(url); if (m.find()) { return true; } } return false; } /** * 效驗(yàn)token是否有效 * * @param request * @return */ private boolean tokenCheck(HttpServletRequest request) { String authToken = request.getHeader("accessToken"); log.info("請(qǐng)求頭中令牌token:{}", authToken); // ...業(yè)務(wù)代碼 return false; } /** * 錯(cuò)誤寫(xiě)出 * * @param response * @param retEnum * @throws IOException */ private void writerError(HttpServletResponse response, String msg, int code) throws IOException { //驗(yàn)證不通過(guò) response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); //將驗(yàn)證不通過(guò)的錯(cuò)誤返回 ObjectMapper mapper = new ObjectMapper(); Map<String, Object> resultMap = new HashMap<>(3); resultMap.put("code", code); resultMap.put("msg", msg); resultMap.put("data", null); response.getWriter().write(mapper.writeValueAsString(resultMap)); } /** * web效驗(yàn)簽名 * * @param request * @return */ public boolean compareSign(HttpServletRequest request) { JSONObject param = JSONUtil.getJSONParam(request); String sign = JSONUtil.getParamRequired(param, String.class, "sign"); // ...業(yè)務(wù)代碼 return s.equals(sign); } /** * APP解密參數(shù) * * @param json * @return */ public JSONObject getJsonParam(String json) { JSONObject jsonParam = JSON.parseObject(json); String aos = jsonParam.getString("aos"); String params = jsonParam.getString("params"); String param = null; if (jsonParam != null && !StringUtils.isEmpty(aos) && !StringUtils.isEmpty(params)) { String key = RSA.rsaDecrypt(aos, "自定義的私鑰"); if (StringUtils.isBlank(key)) { return null; } try { param = AES256.decrypt(params, key); } catch (Exception e) { e.printStackTrace(); } if (StringUtils.isBlank(param)) { return null; } } if (StringUtils.isBlank(param)) { return null; } return JSONObject.parseObject(param); } }
思路都在代碼中備注了,就是在過(guò)濾器中,一層層解析,比如token等,然后再分別解析兩種請(qǐng)求的參數(shù),放入params里,其中用到的兩個(gè)工具類(lèi)如下
JSONUtil
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.MissingServletRequestParameterException; import javax.servlet.http.HttpServletRequest; import java.util.Map; public class JSONUtil { public static JSONObject getJSONParam(HttpServletRequest request){ Map<String, String[]> parameterMap = request.getParameterMap(); JSONObject returnObject = new JSONObject(); for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) { String value = ""; String[] values = entry.getValue(); if (values != null){ for (String s : values) { value = s + ","; } value = value.substring(0, value.length() - 1); } returnObject.put(entry.getKey(), value); } return returnObject; } public static<T> T getParam(JSONObject param, Class<T> tClass, String key){ if (param == null) { return null; } return param.getObject(key, tClass); } public static<T> T getParamRequired(JSONObject param, Class<T> tClass, String key){ if (param == null) { throw new RuntimeException(getErrMsg(key)); } T object = param.getObject(key, tClass); if (object == null){ throw new RuntimeException(getErrMsg(key)); } return object; } private static String getErrMsg(String key) { return "參數(shù)" + key + "必填"; } }
RequestBodyUtil
import java.io.IOException; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; /** * 解析Body數(shù)據(jù) */ public class RequestBodyUtil { private static final int BUFFER_SIZE = 1024 * 8; private RequestBodyUtil(){} public static String read(Reader reader) throws IOException { StringWriter writer = new StringWriter(); try { write(reader, writer); return writer.getBuffer().toString(); } finally { writer.close(); } } public static long write(Reader reader, Writer writer) throws IOException { return write(reader, writer, BUFFER_SIZE); } public static long write(Reader reader, Writer writer, int bufferSize) throws IOException { int read; long total = 0; char[] buf = new char[BUFFER_SIZE]; while ((read = reader.read(buf)) != -1) { writer.write(buf, 0, read); total += read; } return total; } }
注冊(cè)過(guò)濾器我就不說(shuō)了,SpringBoot注冊(cè)過(guò)濾器方式很多,看如何在控制器中接收參數(shù)
@PostMapping("/test") public Result test(@RequestParam String params){ System.out.println("解密后的參數(shù):" + params); return ResponseMsgUtil.success(params); }
名字只要和過(guò)濾器中自定義的Map里的Key對(duì)應(yīng),就會(huì)被拿到參數(shù)
示例:
當(dāng)前url:http://localhost:8080/CarsiLogCenter_new/idpstat.jsp?action=idp.sptopn
request.getRequestURL() http://localhost:8080/CarsiLogCenter_new/idpstat.jsp request.getRequestURI() /CarsiLogCenter_new/idpstat.jsp request.getContextPath()/CarsiLogCenter_new request.getServletPath() /idpstat.jsp request.getQueryString() action=idp.sptopn
public static String getLastAccessUrl(HttpServletRequest request) { StringBuffer requestURL = request.getRequestURI(); String queryString = request.getQueryString(); if (queryString == null) { return requestURL.toString(); } return requestURL + "?" + queryString; }
1、request.getRequestURL()
返回的是完整的url,包括Http協(xié)議,端口號(hào),servlet名字和映射路徑,但它不包含請(qǐng)求參數(shù)。
2、request.getRequestURI()
得到的是request URL的部分值,并且web容器沒(méi)有decode過(guò)的
3、request.getContextPath()
返回 the context of the request.
4、request.getServletPath()
返回調(diào)用servlet的部分url.
5、request.getQueryString()
返回url路徑后面的查詢(xún)字符串
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
在spring boot里面一切配置都是很簡(jiǎn)單的,
看碼吧:
import javax.servlet.http.HttpServletRequest; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.google.gson.Gson; import org.slf4j.Logger; import org.slf4j.LoggerFactory;; @Aspect //定義一個(gè)切面 @Configuration public class LogRecordAspect { private static final Logger logger = LoggerFactory.getLogger(UserInterceptor.class); // 定義切點(diǎn)Pointcut @Pointcut("execution(* com.jiaobuchong.web.*Controller.*(..))") public void excudeService() { } @Around("excudeService()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) ra; HttpServletRequest request = sra.getRequest(); String url = request.getRequestURL().toString(); String method = request.getMethod(); String uri = request.getRequestURI(); String queryString = request.getQueryString(); logger.info("請(qǐng)求開(kāi)始, 各個(gè)參數(shù), url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString); // result的值就是被攔截方法的返回值 Object result = pjp.proceed(); Gson gson = new Gson(); logger.info("請(qǐng)求結(jié)束,controller的返回值是 " + gson.toJson(result)); return result; } }
只要加上上面這個(gè)類(lèi),Aop就算配置好了,不信,去訪問(wèn)以下你的Controller試試。對(duì)比以前配置aop的方式(xml文件),現(xiàn)在的配置都到Java代碼里來(lái)了,@Configuration這個(gè)Annotation就是JavaConfig的典型代表,Spring boot在啟動(dòng)時(shí)會(huì)會(huì)自動(dòng)去加載這些配置,實(shí)現(xiàn)相應(yīng)的配置功能。
關(guān)于“spring boot如何實(shí)現(xiàn)在request里解密參數(shù)返回”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。