这篇文章将为大家详细讲解有关spring boot如何实现在request里解密参数返回,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
有个业务需求,一个请求来源web,一个请求来源APP,web需求验证签名,APP的参数是经过加密,所以出现了两个Controller,除了解密获取参数方式不一样,其他内容一模一样,这样不太合理,所以我决定重构。
思路:既然只是解密不一样,获取到的参数是一样的,那可以写一个过滤器,在里面就把参数解密好,然后返回
Spring Boot在请求的时候是不允许直接修改HttpServletRequest里的paramsMap参数的,但是提供了一个HttpServletRequestWrapper类,继承这个类重写两个方法就可以了。
重写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());
}
/**
* 重载一个构造方法
*
* @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)});
}
}
}
}
思路是重写自定义一个Map存入参数,将解密后需要的参数放入,然后在过滤器中执行这个新的request
过滤器
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("开始过滤器===============");
if (!isFilter(request)) {
writerError(response, RetEnum.RET_TOKEN_ERROR);
return;
}
// 从请求头从获取请求类型,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)) {
// 验证签名,签名错误直接返回
if (!compareSign(request)) {
writerError(response, "签名错误", 500);
return;
}
// 将请求的参数从request中取出,转换成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请求方式比较特殊,所以要从requestBody里读出JSON加密数据
String bodyStr = RequestBodyUtil.read(request.getReader());
// 然后再解密,拿到真正的参数转换成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, "无效的请求来源", 500);
}
}
@Override
public void destroy() {
}
/**
* 筛选
*
* @param request
* @return
*/
private boolean isFilter(HttpServletRequest request) {
if (OPTIONS.equals(request.getMethod())) {
return true;
}
if (isInclude(request)) {
//如果是属于排除的URL,比如登录,注册,验证码等URL,则直接通行
log.info("直接通过");
return true;
}
return tokenCheck(request);
}
/**
* 排除不需要过滤的URL
*
* @param request
* @return
*/
private boolean isInclude(HttpServletRequest request) {
String url = request.getRequestURI().substring(request.getContextPath().length());
log.info("请求url:{}", url);
for (String patternUrl : urlPatterns) {
Pattern p = Pattern.compile(patternUrl);
Matcher m = p.matcher(url);
if (m.find()) {
return true;
}
}
return false;
}
/**
* 效验token是否有效
*
* @param request
* @return
*/
private boolean tokenCheck(HttpServletRequest request) {
String authToken = request.getHeader("accessToken");
log.info("请求头中令牌token:{}", authToken);
// ...业务代码
return false;
}
/**
* 错误写出
*
* @param response
* @param retEnum
* @throws IOException
*/
private void writerError(HttpServletResponse response, String msg, int code) throws IOException {
//验证不通过
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
//将验证不通过的错误返回
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效验签名
*
* @param request
* @return
*/
public boolean compareSign(HttpServletRequest request) {
JSONObject param = JSONUtil.getJSONParam(request);
String sign = JSONUtil.getParamRequired(param, String.class, "sign");
// ...业务代码
return s.equals(sign);
}
/**
* APP解密参数
*
* @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);
}
}
思路都在代码中备注了,就是在过滤器中,一层层解析,比如token等,然后再分别解析两种请求的参数,放入params里,其中用到的两个工具类如下
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 "参数" + key + "必填";
}
}
RequestBodyUtil
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
/**
* 解析Body数据
*/
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;
}
}
注册过滤器我就不说了,SpringBoot注册过滤器方式很多,看如何在控制器中接收参数
@PostMapping("/test")
public Result test(@RequestParam String params){
System.out.println("解密后的参数:" + params);
return ResponseMsgUtil.success(params);
}
名字只要和过滤器中自定义的Map里的Key对应,就会被拿到参数
示例:
当前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协议,端口号,servlet名字和映射路径,但它不包含请求参数。
2、request.getRequestURI()
得到的是request URL的部分值,并且web容器没有decode过的
3、request.getContextPath()
返回 the context of the request.
4、request.getServletPath()
返回调用servlet的部分url.
5、request.getQueryString()
返回url路径后面的查询字符串
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
在spring boot里面一切配置都是很简单的,
看码吧:
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 //定义一个切面
@Configuration
public class LogRecordAspect {
private static final Logger logger = LoggerFactory.getLogger(UserInterceptor.class);
// 定义切点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("请求开始, 各个参数, url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString);
// result的值就是被拦截方法的返回值
Object result = pjp.proceed();
Gson gson = new Gson();
logger.info("请求结束,controller的返回值是 " + gson.toJson(result));
return result;
}
}
只要加上上面这个类,Aop就算配置好了,不信,去访问以下你的Controller试试。对比以前配置aop的方式(xml文件),现在的配置都到Java代码里来了,@Configuration这个Annotation就是JavaConfig的典型代表,Spring boot在启动时会会自动去加载这些配置,实现相应的配置功能。
关于“spring boot如何实现在request里解密参数返回”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。