乐趣区

关于spring:传参统一jwt加密坑

阐明

这个波及到的次要是和前端约定加密形式,我这边被动采纳了 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<String, String[]> 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<String, String[]> requestMap = new HashMap<>(request.getParameterMap());
        // 这边拿到和前端约定的参数名
        String[] secretParams = requestMap.get("secretParam");
        if (secretParams == null) {return;}
        String secretParam = secretParams[0];
        // 没有加密参数的话就不解决了
        if (isNotBlank(secretParam)) {
            //jwt 解析一波
            Jws<Claims> 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<String, Object> 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<String> 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<java.lang.String>
     *
     * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a >
     * @date 2021/2/9
     * @since 1.0.0
     */
    @Override
    public Enumeration<String> getParameterNames() {Vector<String> 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();
        }
    }
}

这边就说下入口和重点办法吧

SpringMVC 源码之参数解析绑定原理 – leanring – 博客园

Debug 最初找到获取参数的中央,这个办法是必须重写的,不然绑定的时候是拿不到的

退出移动版