踩坑紀錄
問題一:
當前後分離項目要進行權限管理時,通常使用JWT來並使用Filter進行權限驗證,當完成代碼後,發現前端傳送的自定義的requestHeaders:Authorization無法由後端的request.getHeader(“Authorization”)獲取,除錯發現是因為跨域問題造成
問題二:
當後端使用過濾器進行token權限驗證,發現當登入後,進入首頁時發送axios請求的請求頭token為null,報401錯誤。
解決方案:
解決問題一:
解決問題的關鍵在於,瀏覽器會在發送 Ajax 請求之前發送一個預請求,確認當前的接口是不是有效的接口,此時的請求方式是 OPTIONS 的請求方式。
因此,JWT 的過濾器需要先判斷該請求是否為預請求,如果是則需要給返回的響應頭中添加跨域相關的信息;如果不是,則按照一般接口進行 JWT 驗證。
當初發現是跨域問題時,簡單想說在Filter類上添加@CrossOrign就好,發現仍然無法獲取請求頭。後來查出在 Filter 上使用 @CrossOrigin 注解是無效的,因為 @CrossOrigin 是 Spring MVC 框架提供的注解,只能用於 Spring MVC 框架處理的請求上。而 Filter 是 Servlet 規範中的一個組件,不依賴於 Spring MVC 框架,因此無法使用 @CrossOrigin 注解。
解決問題的代碼如下:
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {
// 將 /login 加入白名單,即不需要進行 token 權限驗證
private static final String[] WHITE_LIST = {
"/login"
};
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
// 處理瀏覽器的預請求
if (request.getMethod().equals("OPTIONS")) {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST,GET,PUT,OPTIONS,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With,Content-Type,Accept,Authorization,token");
return;
}
String authToken = request.getHeader("Authorization");
String uri = request.getRequestURI();
boolean isWhiteList = Arrays.stream(WHITE_LIST)
.anyMatch(uri::endsWith);
// 如果訪問的是白名單中的 uri,則不進行 token 驗證
if (!isWhiteList) {
if (authToken == null || authToken.isEmpty() || !authToken.startsWith("Bearer")) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Jwt token is required");
return;
}
String token = authToken.substring("Bearer".length()).trim();
// 驗證 token 是否有效,將 token 中包含的用戶信息存入 HttpServletRequest 中
try {
Claims claims = JwtTokenUtils.validateToken(token);
request.setAttribute("claims", claims);
} catch (AuthException e) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
return;
}
}
chain.doFilter(request, response);
}
}
解決問題二:
研究後發現,若將 ***’Authorization’: `Bearer ${getToken()}`***,直接放在請求頭Headers中,首次發送會為空,需頁面刷新才正常,後來將其放到請求攔截器中可正常執行,調整後代碼如下:
function getToken() {
const token = localStorage.getItem('token');
console.log('token',token)
//驗證token是否存在
if (!token) {
return null
}
//驗證token是否超時
const decodedToken = jwt_decode(token);
if (Date.now() >= decodedToken.exp * 1000) {
//若超時,清空localStorage保存的個人資料
localStorage.removeItem('token')
localStorage.removeItem('role')
localStorage.removeItem('username')
localStorage.removeItem('menuData')
localStorage.removeItem('lastLoginTime')
localStorage.removeItem('employee')
}
console.log('token',token)
return token
}
const http = axios.create({
baseURL:'http://localhost:8181/api/',
// timeout: 10000,
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
}
})
// 添加請求攔截器
http.interceptors.request.use(config => {
if (getToken()) {
config.headers['Authorization'] = `Bearer ${getToken()}`;
}
// 在發送請求之前做點什麼
return config;
},
function (error) {
// 對請求做些什麼
return Promise.reject(error);
});
轉載請注明來源,歡迎對文章中的引用來源進行考證,歡迎指出任何有錯誤或不夠清晰的表達。可以郵件至 b8954008@gmail.com