背景
我們有一個(gè)Web項(xiàng)目,這個(gè)項(xiàng)目提供了很多的Rest API。也做了權(quán)限控制,訪問(wèn)API的請(qǐng)求必須要帶上事先認(rèn)證后獲取的Token才可以。
認(rèn)證的話就在Filter中進(jìn)行的,會(huì)獲取請(qǐng)求的Token進(jìn)行驗(yàn)證,如果成功了可以得到Token中的用戶信息,本文的核心就是講解如何將用戶信息(用戶ID)優(yōu)雅的傳遞給API接口(Controller)。
方式一(很挫)
我們?cè)贔ilter中進(jìn)行了統(tǒng)一攔截,在Controller中獲取用戶ID的話,仍然可以再次解析一遍T(mén)oken獲取用戶ID
@GetMapping("/hello")
public String test(HttpServletRequest request) {
String token = request.getHeader("token");
JWTResult result = JWTUtils.checkToken(token);
Long userId = result.getUserId();
}
方式二(優(yōu)雅)
方式一需要重新解析一遍T(mén)oken, 浪費(fèi)資源。我們可以直接將Filter中解析好了的用戶ID直接通過(guò)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"));
}
方式三(很優(yōu)雅)
通過(guò)Header傳遞確實(shí)很方便,但如果你有代碼潔癖的話總會(huì)覺(jué)得怪怪的,能不能不用Header方式,比如說(shuō)我就在方法上定義一個(gè)loginUserId的參數(shù),你給我直接注入進(jìn)來(lái),這個(gè)有點(diǎn)意思哈,下面我們來(lái)實(shí)現(xiàn)下:
GET參數(shù)方式
在Filter中追加參數(shù):
@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
getParameterNames() {
Set
paramNames = new LinkedHashSet<>();
paramNames.add("loginUserId");
Enumeration
names = super.getParameterNames();
while(names.hasMoreElements()) {
paramNames.add(names.nextElement());
}
return Collections.enumeration(paramNames);
}
};
chain.doFilter(requestWrapper, httpResponse);
}
接口中直接填寫(xiě)參數(shù)即可獲取:
@GetMapping("/hello")
public String save2(String name, Long loginUserId) {
// loginUserId 就是Filter中追加的值
}
對(duì)于post請(qǐng)求,也可以用這種方式:
@PostMapping("/hello")
public String save2(User user, Long loginUserId) {
}
可是往往我們?cè)谟胮ost請(qǐng)求的時(shí)候,要么就是表單提交,要么就是json體的方式提交,一般不會(huì)使用get方式參數(shù),這也就意味著這個(gè)loginUserId我們需要注入到對(duì)象中:
先創(chuàng)建一個(gè)參數(shù)實(shí)體類:
public class User {
private String name;
private Long loginUserId;
}
先模擬表單提交的方式,看看行不行:
@PostMapping("/hello")
public User save2(User user) {
return user;
}
用PostMan測(cè)試一下,表單方式是直接支持的:再次試下Json提交方式:
@PostMapping("/hello")
public User save2(@RequestBody User user) {
return user;
}
看下圖,失敗了,得重新想辦法實(shí)現(xiàn)下只需要在HttpServletRequestWrapper中重新對(duì)提交的內(nèi)容進(jìn)行修改即可:
@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直接注入到參數(shù)中了,不用去Header中獲取,是不是很方便。