instructions

This mainly involves the encryption mode agreed with the front end, and I took the initiative to adopt JWT mode. The reason will not be explained.

Implementation of the general idea is to do interception, before trying a version of annotations, do half of a problem. Annotation interception parameters are already bound. If you go to deal with it will be very troublesome.

Then I gave up and decided to use the filter. I thought I would just change the request parameters. In fact, this is actually feasible, but I have to change the getParamNames method at the same time

Let’s start with implementation

Define a filter

package com.fedtech.common.filter.jwt;

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/** * Convert the agreed request header parameters using JWT to normal parameters for the system to use **@author <a href = "mailto:[email protected]" > huan </a >
 * @date 2021/2/9
 * @since1.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(a) {}}Copy the code

Overrides methods that get parameter correlation

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. Interface request filter before and after removing Spaces ** 2. Parse JWT parameters **@author <a href = "mailto:[email protected]" > huan </a >
 * @date 2021/1/26
 * @since1.0.0 * /
@Slf4j
public class ParameterRequestWrapper extends HttpServletRequestWrapper {

    /** * params */ for request.getParam()
    private final Map<String, String[]> params = new HashMap<>();


    /** * JWT is done here, the main idea is to change params, after all, there is no setParam method * this method also handles the whitespace before and after arguments **@paramRequest Request * *@author <a href = "mailto:[email protected]" > huan </a >
     * @date 2021/2/9
     * @since1.0.0 * /
    public ParameterRequestWrapper(HttpServletRequest request) {
        // Pass the request to the parent class so that it can be output when the corresponding method is called. The parent class is implemented in a similar way to the first new method
        super(request);
        // Assign the parameter list to the current Map to hold the parameters in the request
        Map<String, String[]> requestMap = new HashMap<>(request.getParameterMap());
        // Get the parameter name as agreed with the front end
        String[] secretParams = requestMap.get("secretParam");
        if (secretParams == null) {
            return;
        }
        String secretParam = secretParams[0];
        // If there is no encryption parameter, no processing
        if (isNotBlank(secretParam)) {
            // JWT parses a wave
            Jws<Claims> jws = Jwts.parser()
                    .setSigningKey("secret".getBytes(StandardCharsets.UTF_8))
                    .parseClaimsJws(secretParam);
            // Just filter out a few unnecessary parameters on the line la la la !!!!
            jws.getBody().forEach((x, y) -> {
                if(! equalsAny(x,"sub"."exp"."lat"."jti"."iat")) {
                    requestMap.put(x, newString[]{String.valueOf(y)}); }}); }// Save it all here!!
        this.params.putAll(requestMap);
        // This is div, remove the space before and after
        this.modifyParameterValues();
    }

    /** * Override getInputStream request parameters of type POST must pass through the stream to obtain the value **@return javax.servlet.ServletInputStream
     *
     * @author <a href = "mailto:[email protected]" > huan </a >
     * @date 2021/2/9
     * @since1.0.0 * /
    @Override
    public ServletInputStream getInputStream(a) throws IOException {
        // It is a non-JSON type
        if (!super.getHeader(CONTENT_TYPE).equalsIgnoreCase("json")) {
            return super.getInputStream();
        }
        // Is empty and returns directly
        String json = IOUtils.toString(super.getInputStream(), StandardCharsets.UTF_8);
        if (StringUtils.isEmpty(json)) {
            return super.getInputStream();
        }
        log.info("Parameter before conversion: {}", json);
        Map<String, Object> map = StringJsonUtils.jsonStringToMap(json);
        log.info("Parameter after transformation: {}", JSON.toJSONString(map));
        ByteArrayInputStream bis = new ByteArrayInputStream(JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8));
        return new MyServletInputStream(bis);
    }

    /** * removes whitespace from parameter and returns null **@author <a href = "mailto:[email protected]" > huan </a >
     * @date 2021/2/9
     * @since1.0.0 * /
    private void modifyParameterValues(a) {
        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); }}/** * this is the most important, to look at the MVN parsing chain, in {@linkWebUtils# getParametersStartingWith (ServletRequest, String)} the gain parameter is go inside this method * *@return java.util.Enumeration<java.lang.String>
     *
     * @author <a href = "mailto:[email protected]" > huan </a >
     * @date 2021/2/9
     * @since1.0.0 * /
    @Override
    public Enumeration<String> getParameterNames(a) {
        Vector<String> vector = new Vector<>(params.keySet());
        return vector.elements();
    }

    /** * Overrides the getParameter argument to get ** from the map in the current class@return java.lang.String
     *
     * @author <a href = "mailto:[email protected]" > huan </a >
     * @date 2021/2/9
     * @since1.0.0 * /
    @Override
    public String getParameter(String name) {
        String[] values = params.get(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }

    /** * override getParameterValues **@return java.lang.String[]
     *
     * @author <a href = "mailto:[email protected]" > huan </a >
     * @date 2021/2/9
     * @since1.0.0 * /
    @Override
    public String[] getParameterValues(String name) {/ / same as above
        return params.get(name);
    }

    static class MyServletInputStream extends ServletInputStream {
        private final ByteArrayInputStream bis;

        public MyServletInputStream(ByteArrayInputStream bis) {
            this.bis = bis;
        }

        @Override
        public boolean isFinished(a) {
            return true;
        }

        @Override
        public boolean isReady(a) {
            return true;
        }

        @Override
        public void setReadListener(ReadListener listener) {}@Override
        public int read(a) {
            returnbis.read(); }}}Copy the code

Here, let’s talk about the entrance and key methods

SpringMVC source code parameter parsing binding principle – leanRing – blog park

Debug finally finds the place to retrieve the parameter. This method must be overridden otherwise it will not be retrieved during binding