浏览器端JWT令牌传递问题:页面请求403错误及前置验证需求
浏览器端JWT令牌发送与页面权限验证问题
问题现象
- Postman请求正常:携带
Authorization头发送GET请求获取页面时,过滤器仅被调用一次,且成功获取到令牌,后端日志如下:
2024-06-26 17:33:53.036 INFO 6856 --- [nio-8080-exec-3] u.d.P.security.JWTTokenConfig : Filter starts 2024-06-26 17:33:53.037 INFO 6856 --- [nio-8080-exec-3] u.d.P.security.JWTTokenConfig : Jwt Token: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJleHAiOjE3MTk0MTE4MDYsImlhdCI6MTcx OTQwODgwNn0.YOdWUsdkTl4E4ucZ1gjk8sAYKaBMxrsVET5m6rnRXEg 2024-06-26 17:33:53.037 INFO 6856 --- [nio-8080-exec-3] u.d.P.security.JWTTokenConfig : Received JWT token: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJleHAiOjE3MTk0MTE4MDYsImlhdCI6MTcx OTQwODgwNn0.YOdWUsdkTl4E4ucZ1gjk8sAYKaBMxrsVET5m6rnRXEg
- 浏览器刷新页面异常:过滤器被调用三次,仅第二次请求获取到令牌;若将
/admin_office端点设为受保护,过滤器仅被调用一次且未获取到令牌,前端收到403错误。
当前浏览器页面代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Admin Office Page</title> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> </head> <body> <h1>Welcome to Admin Office</h1> <script> function getCookie(name) { let cookieArr = document.cookie.split(";"); for(let i = 0; i < cookieArr.length; i++) { let cookiePair = cookieArr[i].split("="); if(name == cookiePair[0].trim()) { return decodeURIComponent(cookiePair[1]); } } return null; } let jwtToken = getCookie("JWT_TOKEN"); console.log('token '+jwtToken); fetch('/admin_office', { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + jwtToken } }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.text(); }) .then(data => { console.log('Server response:',jwtToken ); }) .catch(error => { console.error('Error sending token to server:', error); }); </script> </body> </html>
浏览器请求时后端日志
2024-06-26 17:40:50.160 INFO 6856 --- [nio-8080-exec-5] u.d.P.security.JWTTokenConfig : Filter starts 2024-06-26 17:40:50.162 INFO 6856 --- [nio-8080-exec-5] u.d.P.security.JWTTokenConfig : Jwt Token: null 2024-06-26 17:40:50.195 INFO 6856 --- [nio-8080-exec-6] u.d.P.security.JWTTokenConfig : Filter starts 2024-06-26 17:40:50.195 INFO 6856 --- [nio-8080-exec-6] u.d.P.security.JWTTokenConfig : Jwt Token: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJleHAiOjE3MTk0MTUwODYsImlhdCI6MT cxOTQxMjA4Nn0.Uwgj-5TuQnFtRxOQBnSVkIiBzTv4hg9swbAXHRX7dec 2024-06-26 17:40:50.196 INFO 6856 --- [nio-8080-exec-6] u.d.P.security.JWTTokenConfig : Received JWT token: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJleHAiOjE3MTk0MTUwODYsImlhdCI6MT cxOTQxMjA4Nn0.Uwgj-5TuQnFtRxOQBnSVkIiBzTv4hg9swbAXHRX7dec 2024-06-26 17:40:50.201 INFO 6856 --- [nio-8080-exec-6] u.d.PetMusicStorage.service.JWTService : Parsed roles from token: [ROLE_ADMIN] 2024-06-26 17:40:50.201 INFO 6856 --- [nio-8080-exec-7] u.d.P.security.JWTTokenConfig : Filter starts 2024-06-26 17:40:50.202 INFO 6856 --- [nio-8080-exec-7] u.d.P.security.JWTTokenConfig : Jwt Token: null 2024-06-26 17:40:50.203 INFO 6856 --- [nio-8080-exec-6] u.d.P.security.JWTTokenConfig : User 1 with roles [ROLE_ADMIN]
问题根源
浏览器刷新页面时,首次请求是获取HTML文件的GET请求,此时前端脚本尚未执行,无法自动添加Authorization请求头,导致该请求无令牌,被过滤器拦截。后续脚本执行后发送的fetch请求才带上令牌,但当端点受保护时,初始页面请求已被拒绝,返回403错误。
而Postman请求是直接在请求头中携带令牌,因此一次请求即可通过验证。
解决方案
方案1:初始页面请求携带令牌(推荐)
修改前端逻辑,在页面加载时先从Cookie获取令牌,携带令牌请求页面内容,根据后端响应展示对应内容:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Admin Office Page</title> </head> <body> <div id="app"></div> <script> function getCookie(name) { let cookieArr = document.cookie.split(";"); for(let i = 0; i < cookieArr.length; i++) { let cookiePair = cookieArr[i].split("="); if(name == cookiePair[0].trim()) { return decodeURIComponent(cookiePair[1]); } } return null; } // 页面初始化函数:验证令牌并获取页面内容 async function initAdminPage() { const jwtToken = getCookie("JWT_TOKEN"); const appContainer = document.getElementById('app'); // 无令牌直接提示 if (!jwtToken) { appContainer.innerHTML = '<h1>未登录,请先登录</h1>'; return; } try { // 携带令牌请求页面 const response = await fetch('/admin_office', { method: 'GET', headers: { 'Authorization': `Bearer ${jwtToken}` } }); // 处理响应 if (!response.ok) { if (response.status === 403) { appContainer.innerHTML = '<h1>权限不足,无法访问此页面</h1>'; } else { throw new Error(`请求失败,状态码:${response.status}`); } return; } // 渲染页面内容 const pageContent = await response.text(); appContainer.innerHTML = pageContent; } catch (error) { appContainer.innerHTML = `<h1>页面加载失败:${error.message}</h1>`; console.error('初始化错误:', error); } } // 页面加载完成后执行初始化 window.addEventListener('load', initAdminPage); </script> </body> </html>
方案2:后端直接拦截无令牌请求
确保后端/admin_office接口为受保护端点,仅接受携带有效JWT令牌的请求:
- 令牌有效:返回完整的管理页面HTML
- 令牌缺失/无效:直接返回403错误页面或重定向到登录页
同时,前端可通过全局请求拦截器自动给所有请求添加令牌:
function getCookie(name) { let cookieArr = document.cookie.split(";"); for(let i = 0; i < cookieArr.length; i++) { let cookiePair = cookieArr[i].split("="); if(name == cookiePair[0].trim()) { return decodeURIComponent(cookiePair[1]); } } return null; } // 全局拦截所有fetch请求,自动添加Authorization头 const originalFetch = window.fetch; window.fetch = async function(url, options = {}) { const jwtToken = getCookie("JWT_TOKEN"); if (jwtToken) { options.headers = { ...options.headers, 'Authorization': `Bearer ${jwtToken}` }; } return originalFetch(url, options); };
内容的提问来源于stack exchange,提问作者PavelPavlik
相关产品推荐
相关产品推荐

