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

ASP.NET Api Controller获取当前登录用户及Session替代方案咨询

解决ASP.NET OAuth登录后无法通过Session获取用户的问题

我明白你遇到的困境:用AngularJS结合ASP.NET OAuth做登录,想把操作日志关联到当前用户,但HttpContext.Current.Session总是为空。这其实是OAuth认证流程和Session机制的执行顺序问题,我给你几个更可靠的替代方案,按推荐程度排序:

1. 把用户信息存入Claims(最推荐)

Claims是ASP.NET认证体系的核心,只要用户通过认证,后续所有请求的ClaimsPrincipal里都会携带这些信息,完全不需要依赖Session,特别适合前后端分离的API场景。

修改认证逻辑添加Claims

在你的GrantResourceOwnerCredentials方法里,获取到用户信息后,把关键字段(比如Emp_ID、用户名)添加到认证票据的Claims中:

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
    ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
    if (user == null)
    {
        context.SetError("invalid_grant", "The user name or password is incorrect.");
        return;
    }

    // 获取你的自定义用户信息
    var userInfo = GetUserInfo(user.UserName);

    ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType);
    // 添加上自定义的Claims
    oAuthIdentity.AddClaim(new Claim("Emp_ID", userInfo.Emp_ID.ToString()));
    oAuthIdentity.AddClaim(new Claim("Emp_UserName", userInfo.Emp_UserName));

    ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager, CookieAuthenticationDefaults.AuthenticationType);
    // Cookie认证的Identity也同步添加Claims
    cookiesIdentity.AddClaim(new Claim("Emp_ID", userInfo.Emp_ID.ToString()));
    cookiesIdentity.AddClaim(new Claim("Emp_UserName", userInfo.Emp_UserName));

    AuthenticationProperties properties = CreateProperties(user.UserName);
    AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
    context.Validated(ticket);
    context.Request.Context.Authentication.SignIn(cookiesIdentity);
}

在日志方法中读取Claims

之后在SaveLog里,直接从当前认证用户的Claims里拿信息就行:

public void SaveLog<T>(T Obj, string Operation)
{
    string hostName = Dns.GetHostName();
    string myIP = Dns.GetHostEntry(hostName).AddressList[0].ToString();
    
    // 从当前用户的Claims中提取Emp_ID
    var currentPrincipal = HttpContext.Current.User as ClaimsPrincipal;
    var empIdClaim = currentPrincipal.FindFirst("Emp_ID");
    if (empIdClaim == null)
    {
        // 这里可以加个异常处理,防止未认证的请求进来
        throw new UnauthorizedAccessException("用户未认证");
    }
    int empId = int.Parse(empIdClaim.Value);
    
    MyLogger.Data = new JavaScriptSerializer().Serialize(Obj);
    MyLogger.OperationType = Operation;
    MyLogger.TableName = typeof(T).Name;
    MyLogger.DateTime = DateTime.Now;
    MyLogger.User_ID = empId;
    MyLogger.IP_Address = myIP;
    db.Loggers.Add(MyLogger);
    Commit();
}

2. 利用你已有的AuthenticationProperties获取用户信息

你已经在CreateProperties里把用户信息序列化后存入了认证属性,其实可以直接从Owin上下文里读取这些属性,不用依赖Session:

public void SaveLog<T>(T Obj, string Operation)
{
    string hostName = Dns.GetHostName();
    string myIP = Dns.GetHostEntry(hostName).AddressList[0].ToString();
    
    // 从Owin上下文获取认证属性
    var owinContext = HttpContext.Current.GetOwinContext();
    var authGrant = owinContext.Authentication.AuthenticationResponseGrant;
    if (authGrant?.Properties?.Dictionary.TryGetValue("User", out string userJson) == true)
    {
        JavaScriptSerializer js = new JavaScriptSerializer();
        var user = js.Deserialize<View_Emps>(userJson);
        MyLogger.User_ID = user.Emp_ID;
    }
    else
    {
        throw new UnauthorizedAccessException("无法获取用户信息");
    }
    
    MyLogger.Data = new JavaScriptSerializer().Serialize(Obj);
    MyLogger.OperationType = Operation;
    MyLogger.TableName = typeof(T).Name;
    MyLogger.DateTime = DateTime.Now;
    MyLogger.IP_Address = myIP;
    db.Loggers.Add(MyLogger);
    Commit();
}

不过要注意:这种方式会把用户信息存在Cookie或者令牌里,如果用户信息比较多,会增大请求体积,所以只适合存少量关键信息。

3. 启用Session(不推荐API场景)

如果你一定要用Session,得确保Session中间件在OAuth中间件之前启动,否则Session还没初始化就执行OAuth逻辑了。

在Startup.cs的Configuration方法里调整顺序:

public void Configuration(IAppBuilder app)
{
    // 先启用Session
    app.UseSession();
    // 再配置认证中间件
    ConfigureAuth(app);
}

同时在Web.config里确认Session配置:

<system.web>
  <sessionState mode="InProc" timeout="20" />
</system.web>

但这种方式不适合API场景,因为API应该是无状态的,Session会引入状态,不利于集群部署和扩展。

总结

优先用Claims方案,这是ASP.NET认证的标准用法,无状态、可靠,完全适配前后端分离的架构。如果需要完整的用户信息,也可以只存Emp_ID在Claims里,需要时再去数据库查询,这样更安全(避免用户信息被篡改)。

内容的提问来源于stack exchange,提问作者Ȝlaa A. Saleh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:49:06