Token認證,如何快速方便獲取用戶信息


背景

我們有一個Web項目,這個項目提供了很多的Rest API。也做了權限控制,訪問API的請求必須要帶上事先認證后獲取的Token才可以。

認證的話就在Filter中進行的,會獲取請求的Token進行驗證,如果成功了可以得到Token中的用戶信息,本文的核心就是講解如何將用戶信息(用戶ID)優雅的傳遞給API接口(Controller)。

方式一(很挫)

我們在Filter中進行了統一攔截,在Controller中獲取用戶ID的話,仍然可以再次解析一遍Token獲取用戶ID

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

方式二(優雅)

方式一需要重新解析一遍Token, 浪費資源。我們可以直接將Filter中解析好了的用戶ID直接通過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();
			}
			return super.getHeader(name);
		}
	};
	chain.doFilter(requestWrapper, httpResponse);
}

接口中直接從Header中獲取解析好了的用戶ID:

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

方式三(很優雅)

通過Header傳遞確實很方便,但如果你有代碼潔癖的話總會覺得怪怪的,能不能不用Header方式,比如說我就在方法上定義一個loginUserId的參數,你給我直接注入進來,這個有點意思哈,下面我們來實現下:

GET參數方式

在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());
				}
				return Collections.enumeration(paramNames);
			}
	};
	chain.doFilter(requestWrapper, httpResponse);
}

接口中直接填寫參數即可獲取:

@GetMapping("/hello")
public String save2(String name, Long loginUserId) {
	// loginUserId 就是Filter中追加的值
}

對於post請求,也可以用這種方式:

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

可是往往我們在用post請求的時候,要么就是表單提交,要么就是json體的方式提交,一般不會使用get方式參數,這也就意味着這個loginUserId我們需要注入到對象中:

先創建一個參數實體類:

public class User {

	private String name;
	
	private Long loginUserId;
}

先模擬表單提交的方式,看看行不行:

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

用PostMan測試一下,表單方式是直接支持的:

再次試下Json提交方式:

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

看下圖,失敗了,得重新想辦法實現下

只需要在HttpServletRequestWrapper中重新對提交的內容進行修改即可:

@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) {
		 
		    }
    };
}

到此為止,我們就可以直接將Token解析的用戶ID直接注入到參數中了,不用去Header中獲取,是不是很方便。

歡迎加入我的知識星球,一起交流技術,免費學習猿天地的課程(http://cxytiandi.com/course)

PS:目前星球中正在星主的帶領下組隊學習Spring Cloud,等你哦!

微信掃碼加入猿天地知識星球

猿天地


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM