说明
这个涉及到的主要是和前端约定加密方式,我这边主动采用了jwt方式,原因吗就不解释了。
实现起来大致思想就是做拦截,之前尝试了一版注解方式,做了一半想到一个问题。。注解拦截的参数是已经绑定之后的了。。要是自己再去处理的话就会非常麻烦。。
后来果断放弃,想了想还是使用过滤方便,想着只要把请求参数改了不就行了,实际上这样确实是可行的,就是有个坑,得同时修改getParamNames方法,不然参数是绑定不了的
先说实现吧
定义一个过滤器
package com.fedtech.common.filter.jwt;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 将约定的请求头参数使用jwt转化为普通参数,供系统使用
*
* @author <a href="mailto:njpkhuan@gmail.com"> huan </a>
* @date 2021/2/9
* @since 1.0.0
*/
@Component
public class JwtFilter implements Filter {
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException {
ParameterRequestWrapper paramsRequest = new ParameterRequestWrapper((HttpServletRequest) arg0);
String[] secretParams = paramsRequest.getParameterMap().get("secretParam");
if (secretParams == null) {
arg2.doFilter(arg0, arg1);
} else {
arg2.doFilter(paramsRequest, arg1);
}
}
@Override
public void init(FilterConfig arg0) {
}
@Override
public void destroy() {
}
}
重写获取参数相关的方法
package com.fedtech.common.filter.jwt;
import com.alibaba.fastjson.JSON;
import com.fedtech.common.util.StringJsonUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import static com.fedtech.common.constants.Request.RequestConstants.CONTENT_TYPE;
import static org.apache.commons.lang3.StringUtils.equalsAny;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* 1. 接口请求过滤去除前后空格
* 2. 解析jwt参数
*
* @author <a href="mailto:njpkhuan@gmail.com"> huan </a>
* @date 2021/1/26
* @since 1.0.0
*/
@Slf4j
public class ParameterRequestWrapper extends HttpServletRequestWrapper {
/**
* 这个东东就是request.getParam()的params
*/
private final Map params = new HashMap<>();
/**
* 解析jwt就是在这边做的,主要思想就是修改params,毕竟官方没提供setParam方法
* 该方法还同时对参数前后空格做了处理
*
* @param request 请求
*
* @author <a href="mailto:njpkhuan@gmail.com"> huan </a>
* @date 2021/2/9
* @since 1.0.0
*/
public ParameterRequestWrapper(HttpServletRequest request) {
// 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似
super(request);
//将参数表,赋予给当前的Map以便于持有request中的参数
Map requestMap = new HashMap<>(request.getParameterMap());
//这边拿到和前端约定的参数名
String[] secretParams = requestMap.get("secretParam");
if (secretParams == null) {
return;
}
String secretParam = secretParams[0];
//没有加密参数的话就不处理了
if (isNotBlank(secretParam)) {
//jwt解析一波
Jws jws = Jwts.parser()
.setSigningKey("secret".getBytes(StandardCharsets.UTF_8))
.parseClaimsJws(secretParam);
//这边只要过滤掉几个不需要的参数就行啦啦啦啦!!!!
jws.getBody().forEach((x, y) -> {
if (!equalsAny(x, "sub", "exp", "lat", "jti", "iat")) {
requestMap.put(x, new String[]{String.valueOf(y)});
}
});
}
//在这边全部存起来哈!!
this.params.putAll(requestMap);
//这个是div的,去除前后空格
this.modifyParameterValues();
}
/**
* 重写getInputStream方法 post类型的请求参数必须通过流才能获取到值
*
* @return javax.servlet.ServletInputStream
*
* @author <a href="mailto:njpkhuan@gmail.com"> huan </a>
* @date 2021/2/9
* @since 1.0.0
*/
@Override
public ServletInputStream getInputStream() throws IOException {
//非json类型,直接返回
if (!super.getHeader(CONTENT_TYPE).equalsIgnoreCase("json")) {
return super.getInputStream();
}
//为空,直接返回
String json = IOUtils.toString(super.getInputStream(), StandardCharsets.UTF_8);
if (StringUtils.isEmpty(json)) {
return super.getInputStream();
}
log.info("转化前参数:{}", json);
Map map = StringJsonUtils.jsonStringToMap(json);
log.info("转化后参数:{}", JSON.toJSONString(map));
ByteArrayInputStream bis = new ByteArrayInputStream(JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8));
return new MyServletInputStream(bis);
}
/**
* 将parameter的值去除空格并且空串返回 null值
*
* @author <a href="mailto:njpkhuan@gmail.com"> huan </a>
* @date 2021/2/9
* @since 1.0.0
*/
private void modifyParameterValues() {
Set set = params.keySet();
for (String key : set) {
String[] values = params.get(key);
String[] newValues = new String[values.length];
for (int i = 0; i < values.length; i++) {
newValues[i] = values[i].trim();
if (newValues[i].length() <= 0) {
newValues[i] = null;
}
}
params.put(key, newValues);
}
}
/**
* 这个吗是最主要的,去看mvn解析链的话,在{@link WebUtils#getParametersStartingWith(ServletRequest, String)}这个里面获取参数就是走的这个方法
*
* @return java.util.Enumeration
*
* @author <a href="mailto:njpkhuan@gmail.com"> huan </a>
* @date 2021/2/9
* @since 1.0.0
*/
@Override
public Enumeration getParameterNames() {
Vector vector = new Vector<>(params.keySet());
return vector.elements();
}
/**
* 重写getParameter 参数从当前类中的map获取
*
* @return java.lang.String
*
* @author <a href="mailto:njpkhuan@gmail.com"> huan </a>
* @date 2021/2/9
* @since 1.0.0
*/
@Override
public String getParameter(String name) {
String[] values = params.get(name);
if (values == null || values.length == 0) {
return null;
}
return values[0];
}
/**
* 重写getParameterValues
*
* @return java.lang.String[]
*
* @author <a href="mailto:njpkhuan@gmail.com"> huan </a>
* @date 2021/2/9
* @since 1.0.0
*/
@Override
public String[] getParameterValues(String name) {//同上
return params.get(name);
}
static class MyServletInputStream extends ServletInputStream {
private final ByteArrayInputStream bis;
public MyServletInputStream(ByteArrayInputStream bis) {
this.bis = bis;
}
@Override
public boolean isFinished() {
return true;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() {
return bis.read();
}
}
}
Debug最后找到获取参数的地方,这个方法是必须重写的,不然绑定的时候是拿不到的