从PC/移动端跳转到网页端时TOKEN失效导致验证失败处理

博客讲述了处理网页跳转权限问题的过程。在其他端口跳转到网页端时,因token未传递,用户操作无权限。通过写跳转页面接收加密信息进行权限验证并存缓存解决。之后发现跨平台跳转仍可能没权限,是因前端token存于浏览器缓存,需在请求前存入,最终解决权限问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以前处理过一个BUG,在其他端口点击跳转到网页端时,用户继续操作时没有权限上传失败、或没有浏览权限跳转到404页面

这个问题的是因为在跳转的过程中token没有传递过来,为了解决这个问题,写了一个跳转页面,接收加密过的用户id、sessionkey、url

在此页面进行权限验证并存入缓存,成功则跳转到url,废话不多说,上代码:

//服务端
<?php
//加密解密服务
require("common/aes.php");

// 定义项目路径
defined('API_ROOT') || define('API_ROOT', dirname(__FILE__) . '/..');
define('VERSION', 'test'); // develop=>开发,test=>测试,product=>正式

// 引入composer
require_once API_ROOT . '/vendor/autoload.php';

// 时区设置
date_default_timezone_set('Asia/Shanghai');
session_start();
$userid = $_GET['u'];
$sessionKey = $_GET['s'];
$go = $_GET['g'];

if (isset($userid) && strlen($userid) > 0 && isset($sessionKey) && strlen($sessionKey) > 0 && isset($go) && strlen($go) > 0) {
    $des = new TripleAES();
    $userid = (int)trim($des->Decrypt($userid));
    $sessionKey = trim($des->Decrypt($sessionKey));
    $go = trim($des->Decrypt($go));

    $checkErrMsg = "";
    $data = DoUserLogin($userid, "", $sessionKey, $checkErrMsg) or die('你当前已离线,请重新登录在刷新重试!' . $userid . '--' . $sessionKey . '---' . $go);
    $nulstr = strstr($go, chr(0));
    if ($nulstr) {
        $go = substr($go, 0, strlen($go) - strlen($nulstr));
    }
    header("Location: " . $go);
} else {
    die('你当前已离线,请重新登录在刷新重试!');
}
//验证并缓存用户数据
function DouserLogin($userid, $password, $sessionKey, &$checkErrMsg)
{

    try {
        $client = \GrpcServer\ClientFactory::createClient(
            'tokencheck.TokenCheck', VERSION
        );
        $req = new \Cfwf\Micro_service\Tokencheck\CheckUserTokenWhenClientAccessRequest();
        $req->setUserid($userid);
        $req->setToken($sessionKey);
        $req->setOs(1);
        list($data) = $client->CheckUserTokenWhenClientAccess($req)->wait();
        $lists = $data->toArray();
        if ($lists['result'] != 1) {
            return false;
        } else {
            $request['os'] = 1;
            $request['user_id'] = $userid;
            $request['token'] = $sessionKey;
            $request['web_level'] = 0;
            $AccountGrpc = new \Common\Domain\AccountGrpc();
            $data = $AccountGrpc->setLoginUserInfo($request, VERSION);
            $_SESSION['loginUserInfo'] = $data;
            return $data;
        }
    } catch (\Exception $e) {
        return false;
    }
}

?>

跳转到新页面的时候,发现有时还是没有权限,经过检查,前端把token存在在浏览器缓存了,刚跨平台跳转当然不会有浏览器缓存,所以在前端请求前需要把token存入浏览器缓存内。

我在vue的钩子方法内,首先调用请求到接口获取用户数据,保证后续的请求都拿到了 token,还是直接上代码。

...
  created() {
    this.id = this.$route.query.id; // 栏目id
    this.a_id = this.$route.query.a_id; // 活动id
    this.getLoginInfo();
  },
.....
  methods: {
    // 获取登录数据
    //这个接口在白名单内,无需token验证
    async getLoginInfo() {
      const data = {
        s: "Common.Account.getLoginUserInfo",
      };
      //调用接口
      let response = await api.post(url.httpUrl, data);
      const res = response.data.data;
      const isLogin = api.eventListener(res);
      this.user_id = localStorage.getItem("ms_userid");
      this.uploadUrl = url.uploadPath; // 文件上传地址
      this.uploadData = {
        UserId: localStorage.getItem("ms_userid"),
        SessionKey: localStorage.getItem("ms_token"),
        OS: 4,
        CheckCode: api.randomString(40),
      };
      this.options = {
        target: url.uploadPath, // 上传地址
        testChunks: false,
        singleFile: false, // 启用单个文件上传。上传一个文件后,第二个文件将超过现有文件,第一个文件将被取消。
        chunkSize: "1048576" * 1, //分块大小 单位(M)
        query: {
          //传参
          UserId: localStorage.getItem("ms_userid"),
          SessionKey: localStorage.getItem("ms_token"),
          OS: 4,
          PluginType: 1,
        },
      };
      this.function1();
    },
},
...
//api.js内方法
    // 监听登录状态做出相应处理,返回是否登录
    eventListener(res) {
        if (res) {
            localStorage.setItem("ms_username", res.username);
            localStorage.setItem("ms_userid", res.userid);
            localStorage.setItem("ms_userface", res.userface);
            localStorage.setItem("ms_schoolid", res.schoolid);
            localStorage.setItem("ms_identity", JSON.stringify(res.identity));
            localStorage.setItem("ms_userAuth", JSON.stringify(res.userAuth));
            localStorage.setItem("ms_schoolname", res.schoolname);
            localStorage.setItem("ms_token", res.token);
            localStorage.setItem("ms_token_endtime", res.token_endtime);
            return true;
        } else {
            //清除缓存
            this.clearLoginInfo();
            return false;
        }

    },
   //api.js 内封装的post请求
    post(url, data) {
        if (!data.userid) {
            data.userid = localStorage.getItem("ms_userid") ? localStorage.getItem("ms_userid") : 1;
        }
        //前端请求携带的token
        data.token = localStorage.getItem("ms_token");
        return axios({
            method: 'post',
            url,
            data: qs.stringify(data),
            timeout: 30000,
            headers: {}
        }).then(function (response) {
            if (response.data.ret != 200) {
                v.$notify({
                    message: "当前页面部分功能暂时不可用,请稍后再试。",
                    offset: 100
                });
                return false;
            }
            return response;
        }
        ).catch(function (error) {
            console.log(error);
            // 网络异常引发的错误
        });
    },

至此,这个因为权限已经解决了。

 

 

 

 

 

 

 

 

 

<think>嗯,我现在需要解决的是JWTFilter过滤验证失败后,前端跳转token值被设置为零的问题。让我先理清楚整个流程。 首先,JWTFilter通常负责在请求到达后端之前验证Token的有效性。如果验证失败,比如Token过期、签名错误或者被篡改,过滤器应该返回一个错误响应,让前端处理后续的跳转逻辑。但现在前端在跳转token被清零,这可能涉及到几个方面的问题。 可能的原因之一是后端在验证失败后,返回的响应中可能直接清除了Token,或者前端在接收到错误状态码后主动清除了Token。比如,后端可能在响应头或Body里添加了某个指令,让前端删除本地存储的Token。或者,前端代码在捕获到401或403状态码,执行了清除Token的操作,然后跳转到登录页。 另外,也有可能是后端在验证失败,不仅返回错误信息,还设置了某些Cookie或Header,导致前端自动清空Token。例如,如果后端在响应中设置了Set-Cookie头将token设置为空或过期,浏览器可能会自动删除该Cookie,导致前端读取得到零值。 还有可能是前端在处理错误响应,没有正确处理返回的数据,误将Token字段设置为零。比如,前端可能在接收到错误后,执行了类似localStorage.setItem('token', null)或者类似的代码,导致存储的值被覆盖为零或空值。 接下来,我需要分步骤检查。首先,查看JWTFilter的代码,看看在验证失败,后端返回了什么状态码和响应内容。是否在响应中明确修改了Token相关的数据?比如,是否调用了response.setHeader或者其他方法影响前端的Token存储。 然后,检查前端处理HTTP响应的拦截器部分。比如,在使用Axios的情况下,是否在响应拦截器中判断了状态码,并在401情况下清除Token。例如: ```javascript axios.interceptors.response.use(response => { return response; }, error => { if (error.response.status === 401) { localStorage.removeItem('token'); // 或者设置为null router.push('/login'); } return Promise.reject(error); }); ``` 如果有类似代码,就可能导致Token被清除。另外,如果后端返回的响应中包含特定的消息体,前端可能根据消息内容执行清除操作。 另一个可能性是跨域问题。如果前端和后端跨域,且Token存储在Cookie中,而后端在设置Cookie没有正确配置SameSite或Secure属性,可能导致浏览器拒绝保存或修改Cookie,从而出现意外情况。不过这种情况可能导致Token无法传递,而不是被清零。 还需要检查网络请求的响应内容,使用浏览器的开发者工具查看验证失败返回的响应头和响应体,确认是否有指示前端清除Token的指令。例如,响应头中是否有Set-Cookie: token=; expires=Thu, 01 Jan 1970 00:00:00 GMT这样的内容,这会直接让浏览器删除Cookie。 如果是本地存储(如localStorage或sessionStorage),则后端无法直接操作,必须由前端代码主动清除。因此,如果Token存储在这里,问题可能出在前端的错误处理逻辑上,比如在接收到特定错误码执行了清除操作。 总结可能的问题点: 1. 后端在验证失败返回了清除Token的指令(如Set-Cookie)。 2. 前端拦截器在收到错误状态码后主动清除Token。 3. 前端代码逻辑错误,在跳转前误将Token设置为零。 4. Token存储方式(Cookie/LocalStorage)的不同处理导致的问题。 解决步骤建议: 1. 检查JWTFilter的验证失败处理逻辑,确认是否返回了401/403状态码,以及响应头中是否有涉及Token的操作。 2. 检查前端HTTP拦截器代码,查看在接收到错误状态码处理逻辑,特别是是否有清除Token的代码。 3. 使用浏览器开发者工具查看网络请求的响应详情,确认后端返回的具体内容和头部信息。 4. 检查前端跳转前的代码,是否有显式设置Token为零的操作。 5. 如果是Cookie存储,检查后端是否在响应中设置了过期的Cookie导致浏览器删除。 如果确认是前端在错误处理主动清除,可能需要调整逻辑,仅在适当的候清除Token,比如在用户主动退出登录。如果是后端无意中设置了清除指令,需修改后端代码避免这种情况。此外,确保前后端在Token失效处理逻辑一致,比如返回特定的错误码,由前端决定如何处理Token。</think>### 问题分析:JWTFilter验证失败后前端Token被清零的可能原因 1. **后端响应触发前端清除Token** - **表现**:后端返回401/403状态码,前端拦截器自动清除Token。 - **检查点**: - 后端是否在响应头中设置`Set-Cookie: token=空值`(如过期间设为1970)? - 前端是否在拦截器中监听401/403状态码并执行`localStorage.removeItem('token')`? 2. **前端代码逻辑错误** - **表现**:前端在跳转登录页前,错误地将Token赋值为`null`或`0`。 - **检查点**: - 跳转代码中是否有类似`localStorage.setItem('token', null)`的操作? - Token存储类型是否被误转为数字(如`token=0`表示无效)? 3. **跨域或Cookie配置问题** - **表现**:跨域请求中Cookie被浏览器拦截,导致Token无法传递。 - **检查点**: - 后端是否配置`Access-Control-Allow-Credentials: true`和`Access-Control-Allow-Origin`为具体域名? - Cookie的`SameSite`和`Secure`属性是否与部署环境匹配(如HTTPS必须用Secure)? --- ### 逐步排查指南 #### 第一步:确认后端响应内容 1. **查看JWTFilter的验证失败处理逻辑** - 确保返回正确的状态码(如401),但**不要主动修改Token值**。 - 示例代码(Java): ```java response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 仅设置401,不操作Cookie/Header response.getWriter().write("Token验证失败"); ``` 2. **通过浏览器开发者工具检查网络响应** - 打开浏览器控制台 → Network → 找到失败的请求 → 查看**Response Headers**和**Cookies**: - 如果发现`Set-Cookie: token=...`,说明后端主动修改了Cookie。 - 检查响应Body是否包含额外指令(如`{ "code": 401, "clearToken": true }`)。 #### 第二步:检查前端拦截器和存储逻辑 1. **定位HTTP响应拦截器代码** - 示例(Axios): ```javascript axios.interceptors.response.use( response => response, error => { if (error.response.status === 401) { // 确认是否此处清除了Token localStorage.removeItem('token'); // 或设置为null/0 router.push('/login'); } return Promise.reject(error); } ); ``` - **修改建议**:仅跳转页面,暂不清除Token,观察是否仍被清零。 2. **检查Token存储方式** - **Cookie**:后端可能通过响应头修改。 - **localStorage/sessionStorage**:只有前端代码可操作。 - 搜索前端项目中所有`localStorage.setItem('token', ...)`的调用位置。 #### 第三步:验证Token传递流程 1. **正常登录流程** - Token如何存储?登录成功后是否正确写入? ```javascript // 正确示例:从响应数据获取Token并存储 login().then(res => { localStorage.setItem('token', res.data.token); }); ``` 2. **异常流程** - 在Token验证失败后,手动检查本地存储: - 触发401错误 → 不执行任何操作 → 检查Application(开发者工具)中的Token是否仍然存在。 - 如果Token被清除,说明前端有主动清除逻辑;否则可能是后端触发的。 --- ### 常见解决方案 1. **修复前端拦截器逻辑** - 仅在用户主动退出清除Token验证失败跳转: ```javascript // 修改拦截器代码 error => { if (error.response.status === 401) { router.push('/login'); // 先跳转,不清Token } } ``` 2. **后端避免操作Token** - 确保JWTFilter验证失败**不设置Cookie过期或空值**,仅返回状态码: ```java // 避免此类操作 Cookie cookie = new Cookie("token", null); cookie.setMaxAge(0); response.addCookie(cookie); ``` 3. **前后端统一Token失效策略** - 定义标准化错误码(如`{ code: 401, message: "请重新登录" }`),由前端决定是否清除Token。 --- ### 最终验证 1. **模拟Token失效** - 手动修改本地Token为错误值 → 触发请求 → 观察网络响应和前端行为。 - 若Token未清零,则问题可能出在后端;若清零,检查前端拦截器。 2. **日志调试** - 在关键位置添加日志: ```javascript // 前端拦截器 console.log('触发401,当前Token:', localStorage.getItem('token')); // 后端Filter System.out.println("验证失败,返回401"); ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值