background

We have a Web project that provides a number of Rest apis. Access control is also implemented so that requests to access the API must carry tokens obtained after prior authentication.

Authentication is carried out in Filter, which obtains the requested Token for verification. If successful, the user information in the Token can be obtained. The core of this paper is to explain how to gracefully transfer the user information (user ID) to the API interface (Controller).

Method 1 (Sucks)

We have carried out a unified interception in Filter. If the user ID is obtained in Controller, the Token can still be resolved again to obtain the user ID

@GetMapping("/hello")
public String test(HttpServletRequest request) {
    String token = request.getHeader("token");
    JWTResult result = JWTUtils.checkToken(token);
    Long userId = result.getUserId();
}
Copy the code

Method 2 (Elegant)

Method 1 requires the Token to be parsed again, which wastes resources. We can pass the user ID parsed in Filter directly to the interface through the Header.

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
	HttpServletResponse httpResponse = (HttpServletResponse) response;
    String token = request.getHeader("token");
    JWTResult result = JWTUtils.checkToken(token);
    Long userId = result.getUserId();
	HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest) {
		@Override
		public String getHeader(String name) {
			if (name.equals("loginUserId")) {
				return userId .toString();
			}
			returnsuper.getHeader(name); }}; chain.doFilter(requestWrapper, httpResponse); }Copy the code

The interface gets the parsed user ID directly from the Header:

@GetMapping("/hello")
public String save2(HttpServletRequest request) {
	Long userId = Long.parseLong(request.getHeader("loginUserId"));	
}
Copy the code

Method 3 (elegant)

It’s nice to pass it through the Header, but if you’re a code freakish, you might feel a little weird if you don’t use the Header. Let’s say I define a loginUserId parameter on the method, and you inject it in.

GET parameter mode

Append parameters to Filter:

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
	HttpServletResponse httpResponse = (HttpServletResponse) response;
    String token = request.getHeader("token");
    JWTResult result = JWTUtils.checkToken(token);
    Long userId = result.getUserId();
	HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest) {
		    @Override
			public String[] getParameterValues(String name) {
				if (name.equals("loginUserId")) {
					return new String[] { userId .toString() };
				}
				return super.getParameterValues(name);
			}
			@Override
			public Enumeration<String> getParameterNames() {
				Set<String> paramNames = new LinkedHashSet<>();
				paramNames.add("loginUserId");
				Enumeration<String> names =  super.getParameterNames();
				while(names.hasMoreElements()) {
					paramNames.add(names.nextElement());
				}
				returnCollections.enumeration(paramNames); }}; chain.doFilter(requestWrapper, httpResponse); }Copy the code

Directly fill in the parameters in the interface to obtain:

@GetMapping("/hello") public String save2(String name, Long loginUserId) {// loginUserId is the value appended to Filter}Copy the code

For POST requests, you can also do this:

@PostMapping("/hello")
public String save2(User user, Long loginUserId) {
	
}
Copy the code

However, when we use post requests, either as form submissions or as json bodies, we don’t use get arguments, which means we need to inject the loginUserId into the object:

Create a parameter entity class:

public class User {

	private String name;
	
	private Long loginUserId;
}
Copy the code

Simulate form submission to see if it works:

@PostMapping("/hello")
public User save2(User user) {
	return user;
}
Copy the code

Testing with PostMan, the form mode is supported directly:

Try Json submission again:

@PostMapping("/hello")
public User save2(@RequestBody User user) {
	return user;
}
Copy the code

If you look at the picture below, it failed and you have to find a way to implement it again

Only need to modify the content of the submitted in HttpServletRequestWrapper can:

@Override
public ServletInputStream getInputStream() throws IOException {
	byte[] requestBody = new byte[0];
	try {
		requestBody = StreamUtils.copyToByteArray(request.getInputStream());
		Map map = JsonUtils.toBean(Map.class, new String(requestBody));
		map.put("loginUserId", loginUserId);
		requestBody = JsonUtils.toJson(map).getBytes();
	} catch (IOException e) {
		throw new RuntimeException(e);
	}
	final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
	return new ServletInputStream() {
		 @Override
		  public int read() throws IOException {
		       return bais.read();
		  }
		 
		  @Override
		   public boolean isFinished() {
		       return false;
		   }
		 
		   @Override
		   public boolean isReady() {
		        return true;
		   }
		 
		   @Override
		    public void setReadListener(ReadListener listener) {
		 
		    }
    };
}
Copy the code

Insert user ID from Token resolution into parameter. Insert user ID from Token resolution into parameter.