You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET MVC服务器端Cookie过期引发空引用异常问题

问题诊断与解决方案

核心问题分析

上线后出现的空引用错误,确实是**roleId Cookie过期/丢失**导致的:

  • 登录时创建的roleId Cookie未设置过期时间,默认是会话级Cookie——当用户长时间无操作、浏览器后台被回收,或服务器会话超时后,该Cookie会被自动清除
  • NavigationPartial方法直接调用role.Value,未做空值判断,一旦Cookie丢失就触发空引用异常
  • 本地测试时会话不会轻易中断,因此问题不会暴露;服务器环境下用户会话持续周期更长,超时后问题显现

修复步骤

1. 给roleId Cookie设置持久化过期时间

在登录代码中,创建roleId Cookie时,同步设置与Forms认证Cookie一致的过期时间,避免Cookie提前失效:

HttpCookie role = new HttpCookie("roleId", roleId.ToString());
// 设置过期时间,与FormsAuthCookie保持一致(示例为7天,可根据业务调整)
role.Expires = DateTime.Now.AddDays(7);
// 开启HttpOnly和Secure提升安全性
role.HttpOnly = true;
role.Secure = Request.IsSecureConnection; // 生产环境建议强制设为true(需配置HTTPS)
Response.Cookies.Add(role);

2. 增加空值检查,避免空引用异常

修改NavigationPartial方法,先判断Cookie是否存在,再执行后续逻辑:

public PartialViewResult NavigationPartial()
{
    List<KategoriModel> MenuKategoriModel = new List<KategoriModel>();

    try
    {
        HttpCookie role = Request.Cookies.Get("roleId");
        // 空值检查:Cookie不存在时引导用户重新登录
        if (role == null || string.IsNullOrEmpty(role.Value))
        {
            return RedirectToAction("Index", "Login");
        }
        
        int roleId = Convert.ToInt32(role.Value);
        // 后续数据库查询代码保持不变...
    }
    catch (Exception ex)
    {
        Logger.LogRegister(ex.Message + " 导航分部视图部分:", ex.StackTrace, ex.Source);
        return PartialView();
    }

    return PartialView(MenuKategoriModel);
}

3. 优化方案:从Forms认证Cookie中解析角色ID

无需单独存储roleId Cookie,直接从Forms认证的UserData中解析角色信息,避免多Cookie不一致问题:

  • 登录时精简UserData(务必移除明文密码):
// 只存储必要信息,移除明文密码
string userData = $"{roleId}&{email}"; 
FormsAuthentication.SetAuthCookie(userData, true);
  • 在NavigationPartial中解析角色ID:
public PartialViewResult NavigationPartial()
{
    List<KategoriModel> MenuKategoriModel = new List<KategoriModel>();

    try
    {
        if (User.Identity.IsAuthenticated)
        {
            FormsIdentity identity = (FormsIdentity)User.Identity;
            FormsAuthenticationTicket ticket = identity.Ticket;
            string userData = ticket.UserData;
            
            string[] userDataParts = userData.Split('&');
            if (userDataParts.Length >=1 && int.TryParse(userDataParts[0], out int roleId))
            {
                // 后续数据库查询代码保持不变...
            }
            else
            {
                // 数据格式异常,引导重新登录
                return RedirectToAction("Index", "Login");
            }
        }
        else
        {
            // 用户未认证,引导登录
            return RedirectToAction("Index", "Login");
        }
    }
    catch (Exception ex)
    {
        Logger.LogRegister(ex.Message + " 导航分部视图部分:", ex.StackTrace, ex.Source);
        return PartialView();
    }

    return PartialView(MenuKategoriModel);
}

安全建议

  • 立即移除UserData中的明文密码,修改密码时直接从数据库查询用户信息,避免泄露风险
  • 生产环境强制开启Cookie的Secure属性(需配置HTTPS),防止Cookie被明文传输窃取
  • 导航部分的数据库查询存在SQL注入风险,改为参数化查询:
string query = @"SELECT MENU_ID,MENU_AD, PARENT_ID, CONTROLLER_AD, MENU_ICON, ACTIVE_FLAG, ACTION_AD 
FROM ( SELECT MENU_ID,PARENT_ID, CONTROLLER_AD,MENU_AD, MENU_ICON, ACTIVE_FLAG, ACTION_AD, 
ROW_NUMBER() OVER (PARTITION BY MENU_ID ORDER BY 1) AS rn 
FROM Z_TMP_PORTAL_MENU ztpm  
START WITH MENU_ID IN (SELECT MENU_ID FROM Z_TMP_PORTAL_MENUROLE_RS ztpmr WHERE ROLE_ID = :roleId AND ACTIVE_FLAG='Y' )  
CONNECT BY PRIOR PARENT_ID = MENU_ID) WHERE rn = 1";

using (OracleCommand cmd = new OracleCommand(query, con))
{
    cmd.Parameters.Add(":roleId", OracleDbType.Int32).Value = roleId;
    // 后续执行代码...
}

内容的提问来源于stack exchange,提问作者Osman TÜRK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 16:25:01