Use Filter and interceptor to dynamically transfer user information to the Request method

Foreword:

  • During development, it is often necessary to verify the user's login status and obtain user information. If you manually call the user information query interface every time, it will be very cumbersome and the code is redundant. In order to improve development efficiency, this article is here today.

Ideas:

  • When the user requests our method, a Token will be carried, and the member information will be checked out through the Filter filter and put into the request request parameter. Then, in the request method of the Cotroller layer, a parameter of type MemberDeatails is received, and the member information can be obtained directly.

detailed steps:

1. Gradle introduces the required Jar package:
compile "com.fasterxml.jackson.core:jackson-databind:2.8.10"
2. Define a Login annotation
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Login {

    String value() default "";

}
3. Define a MemberDetails.class to encapsulate user information
public class MemberDetails {

    private String memberId;

    private String memberName;

    private String memberNickname;

    private String memberPhone;

    private String memberEmail;

}
5. Define a member interface class
/**
 * @Author: XiongFeng
 * @Description: 会员接口
 * @Date: Created in 19:40 2018/4/10
 */
public interface MemberService {

    /** 根据TokenId获取用户信息 */
    MemberDto getMemberByToken(String token);
}
6. Define a member interface implementation class, and write the user information acquisition method in it
@Service
public class MemberServiceImpl implements MemberService {

    @Override
    public MemberDetails getMemberDetailsByToken(String token) {
        if (StringUtils.isBlank(token)) return null;
        if (!"123".equals(token)) return null;
        MemberDetails memberDetails = new MemberDetails();
        memberDetails.setMemberId("123");
        memberDetails.setMemberName("哈哈123");
        memberDetails.setMemberEmail("[email protected]");
        memberDetails.setMemberNickname("Seifon");
        memberDetails.setMemberPhone("13100001111");
        return memberDetails;
    }
}
7. Define a Request request wrapper class
  • By inheriting the HttpServletRequestWrapper class, rewriting multiple methods in it, and re-encapsulating the parameters passed from the front end. Because in Filter, although you can get a map with parameters through request.getParameterMap(), you cannot directly modify the contents of the request. Once you modify it again, an error will be reported. Later, I found that j2ee has provided us with a solution, using the HttpServletRequestWrapper class to solve the function of adding extra parameters to the request. So I repackage HttpServletRequest, redefine a map in it, put the previous parameters in, and put the parameters we need to add to achieve the effect we want.
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

/**
 * @Author: XiongFeng
 * @Description: 对Request请求重新包装
 * @Date: Created in 11:17 2018/4/13
 */
public class ParameterRequestWrapper extends HttpServletRequestWrapper {
    private Map<String , String[]> params = new HashMap<String, String[]>();


    @SuppressWarnings("unchecked")
    public ParameterRequestWrapper(HttpServletRequest request) {
        // 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似
        super(request);
        //将参数表,赋予给当前的Map以便于持有request中的参数
        this.params.putAll(request.getParameterMap());
    }
    //重载一个构造方法
    public ParameterRequestWrapper(HttpServletRequest request , Map<String , Object> extendParams) {
        this(request);
        addAllParameters(extendParams);//这里将扩展参数写入参数表
    }

    /**
     * 复写获取key的方法
     */
    @Override
    public Enumeration getParameterNames() {
        Vector names = new Vector(params.keySet());
        return names.elements();
    }

    /**
     * 复写获取值value的方法
     */
    @Override
    public String getParameter(String name) {
        Object v = params.get(name);
        if (v == null) {
            return null;
        } else if (v instanceof String[]) {
            String[] strArr = (String[]) v;
            if (strArr.length > 0) {
                return strArr[0];
            } else {
                return null;
            }
        } else if (v instanceof String) {
            return (String) v;
        } else {
            return v.toString();
        }
    }

    @Override
    public String[] getParameterValues(String name) {
        Object v = params.get(name);
        if (v == null) {
            return null;
        } else if (v instanceof String[]) {
            return (String[]) v;
        } else if (v instanceof String) {
            return new String[] { (String) v };
        } else {
            return new String[] { v.toString() };
        }
    }

    public void addAllParameters(Map<String , Object>otherParams) {//增加多个参数
        for(Map.Entry<String , Object>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)});
            }
        }
    }

}
8. Define a filter
  • In this filter, it is mainly to check whether the Token is valid and add the member information to the request. First, get the Token passed from the front end from the Request request header, and use the Token to call the member information acquisition interface to get the user's information, and then put the user information into the ParameterMap. This ParameterMap is a map we repackaged through ParameterRequestWrapper. Therefore, you can add the parameters of the member in it, and then pass the new request.
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: XiongFeng
 * @Description: 会员登录信息过滤器
 * @Date: Created in 11:17 2018/4/13
 */
@Component
@WebFilter(urlPatterns = "/*")
public class MemberFilter implements Filter {

    MemberService memberService = new MemberServiceImpl();

    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;

        String tokenId = req.getHeader("X-Authorization");

        if (tokenId == null || "".equals(tokenId) || tokenId.isEmpty()) {
            chain.doFilter(request, response);
            return;
        }

        MemberDetails memberDetails = memberService.getMemberDetailsByToken(tokenId);
        if (memberDetails == null) {
            try {
                respFail(response);
                return;
            } catch (Exception e) {
                throw new ServletException();
            }
        }

        Map<String, String[]> parameterMap = new HashMap<String, String[]>(request.getParameterMap());
        parameterMap.put("memberId", new String[]{memberDetails.getMemberId()});
        parameterMap.put("memberName", new String[]{memberDetails.getMemberName()});
        parameterMap.put("memberEmail", new String[]{memberDetails.getMemberEmail()});
        parameterMap.put("memberNickname", new String[]{memberDetails.getMemberNickname()});
        parameterMap.put("memberPhone", new String[]{memberDetails.getMemberPhone()});
        chain.doFilter(new ParameterRequestWrapper((HttpServletRequest) req, parameterMap), response);
    }

    /** 返回失败结果Json数据 */
    private void respFail(ServletResponse response) throws Exception {
        Map<String, Object> map = new HashMap<>();
        map.put("status", 500);
        map.put("message", "登录失效,请登录");
        map.put("data", null);
        String s = objectMapper.writeValueAsString(map);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(s);
    }

    @Override
    public void destroy() {

    }
}
9. Define a SpringMVC Interceptor
package cn.seifon.paymodle.interceptor;

import cn.seifon.paymodle.annotations.Login;
import cn.seifon.paymodle.dto.MemberDetails;
import cn.seifon.paymodle.service.manager.member.MemberService;
import cn.seifon.paymodle.service.manager.member.impl.MemberServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: XiongFeng
 * @Description: 会员登录信息拦截器
 * @Date: Created in 11:17 2018/4/13
 */
public class MemberInterceptor extends HandlerInterceptorAdapter {

    ObjectMapper objectMapper = new ObjectMapper();

    MemberService memberService = new MemberServiceImpl();

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        HandlerMethod method = (HandlerMethod) handler;
        String[] memberIds = request.getParameterValues("memberId");

        MethodParameter[] methodParameters = method.getMethodParameters();
        //判断方法类是否有MemberDetails入参
        if (methodParameters.length > 0) {
            for (MethodParameter methodParameter : methodParameters) {
                Type genericParameterType = methodParameter.getGenericParameterType();
                String typeName = genericParameterType.getTypeName();
                if (!typeName.equals(MemberDetails.class.getTypeName())) continue;
                if (memberIds == null || memberIds.length <= 0) return this.respFail(response); //如果找不到用户信息就返回失败
                break;
            }
        }

        //判断是否有Login注解
        Login login = method.getMethodAnnotation(Login.class);
        if (login == null) return true;

        if (memberIds == null || memberIds.length <= 0) return this.respFail(response); //如果找不到用户信息就返回失败
        return true;
    }


    /** 返回失败结果Json数据 */
    private boolean respFail(HttpServletResponse response) throws Exception {
        Map<String, Object> map = new HashMap<>();
        map.put("status", 500);
        map.put("message", "登录失效,请登录");
        map.put("data", null);
        String s = objectMapper.writeValueAsString(map);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(s);
        return false;
    }

}
10. Register the interceptor in WebMvcConfigurer
@Configuration
public class MyWebAppConfigurer
        extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addPathPatterns 用于添加拦截规则
        registry.addInterceptor(new MemberInterceptor()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }

}
11. Define Member Controller
  • After a filter and an interceptor, the request finally came to our Controller layer. At this time, we only need to write MemberDetails memberDetails in the method and it's OK. We can get member information without doing any operations. Isn't it convenient to fry chicken! You can also use the @Login annotation on the method .
@RestController
public class MemberController {


    @RequestMapping("/token")
    @Login
    public Map<String, Object> getUser(MemberDetails memberDetails) {
        //User user = userManager.selectByPrimaryKey(id);
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("status", 200);
        map.put("message", "请求成功");
        map.put("data", memberDetails);
        return map;
    }
    
}

operation result:

Pit encountered:

  • At that time, I tried to put the member parameters into the Attribute in the session field, and I also tried to set the Attribute in the Model. It was later found that this does not work, and it is invalid to use request.setAttribute() directly in the filter. It is also feasible to put it in the Model, but the method in the Controller needs to add @ModelAttribute ("...") to get the user information, which is very inconvenient. Only through request.getParameterMap() put() is the most convenient.

  • At first, I didn't expect to use a filter, so I tried to wrap the request directly through the ParameterRequestWrapper in the interceptor, and later found that no matter what I did, it was unsuccessful. I was very desperate at the time, and then I thought about whether the interceptor did not support repackaging requests, so I did it through the filter, but I did not expect it to be successful. At this time, I thought that since the filter was used, I would simply get the @Login annotation and get the method parameters directly in the filter . Later, I found that the method information could not be obtained in the filter, and I cried. Later, I thought of a way to pass the filter first and then the interceptor. So it worked!

postscript:

  • This article is just a record of my little experience. If there is anything wrong or there is a better way, please leave a message in the comments to correct it!

Reference article: http://www.importnew.com/19023.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324871251&siteId=291194637