Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ASP.Net核心MVC标识-添加临时(会话)声明_C#_Asp.net Mvc_Asp.net Core Mvc_Asp.net Identity - Fatal编程技术网

C# ASP.Net核心MVC标识-添加临时(会话)声明

C# ASP.Net核心MVC标识-添加临时(会话)声明,c#,asp.net-mvc,asp.net-core-mvc,asp.net-identity,C#,Asp.net Mvc,Asp.net Core Mvc,Asp.net Identity,首先,这个问题也被问到这里()和这里(),但这两个问题都没有得到答案,所以我正在尝试恢复它 我正在创建一个多租户web应用程序。用户登录后,可以看到与自己公司相关的数据(但不能看到使用该应用程序的其他公司的数据)。由于特许经营集团拥有多个店面等,单个用户需要访问多个不同公司的情况并不少见,但在这种情况下,他们在登录时必须选择单个公司 几乎所有的数据查询都需要公司ID作为参数,因此我需要一种方便的方法来检查用户当前登录的公司。如果他们注销并登录到不同的公司,我需要查看不同的公司ID。我希望将其存储

首先,这个问题也被问到这里()和这里(),但这两个问题都没有得到答案,所以我正在尝试恢复它

我正在创建一个多租户web应用程序。用户登录后,可以看到与自己公司相关的数据(但不能看到使用该应用程序的其他公司的数据)。由于特许经营集团拥有多个店面等,单个用户需要访问多个不同公司的情况并不少见,但在这种情况下,他们在登录时必须选择单个公司

几乎所有的数据查询都需要公司ID作为参数,因此我需要一种方便的方法来检查用户当前登录的公司。如果他们注销并登录到不同的公司,我需要查看不同的公司ID。我希望将其存储为身份声明,以便在会话期间可以看到,但我不一定要将其存储到数据库中,因为每次登录时它都可能会发生变化

以下是我的登录操作(基于标准ASP.Net核心MVC模板):

[HttpPost]
[异名]
[ValidateAntiForgeryToken]
公共异步任务登录(LoginViewModel模型,字符串returnUrl=null)
{
ViewData[“ReturnUrl”]=ReturnUrl;
if(ModelState.IsValid)
{
var result=wait _signInManager.PasswordSignInAsync(model.Email、model.Password、model.RememberMe、lockoutOnFailure:false);
if(result.successed)
{
//开始自定义代码-检查用户可以访问哪些公司
IEnumerable allcompanys=\u myDbService.getusercompanys(model.Email);
如果(AllCompanys.Count()==1)
{
//然后他们只能访问一家公司——自动登录。
//我需要与用户轻松访问它的ID,因为几乎每个db查询都使用它。
int companyID=所有公司[0];
((ClaimsIdentity)User.Identity).AddClaim(新声明(“CompanyID”,CompanyID.ToString());//这在以后的控制器操作中显示为null。
Debug.WriteLine(“在此处设置断点以检查用户”);//我在调试器中看到1个声明(我的自定义声明),但它尚未在数据库中。
//未来的控制器操作将向我显示3个声明(nameidentifier、name和security stamp),但User.claims.First(c=>c.Type==“CompanyID”)。值抛出错误。
返回重定向到本地(returnUrl);
}
其他的
{
//用户可以访问多个公司-让他们选择一个来完成登录过程
重定向到操作(“选择公司”,新建{companyList=allcompanys});
}
//结束自定义代码
}
//为简洁起见,省略了剩余代码
}
//如果我们走到这一步,有些东西失败了,重新显示形式
返回视图(模型);
}

所以我的问题是:为什么定制不声明“粘贴”,以便在未来的控制器操作中可以看到它?如果这是标准行为,为什么不保存到数据库中?有没有办法在会话期间不使用AddSession()保持此值不变?如果我将此作为声明来实现,是否每次访问该值时都要往返数据库?

对于其他有此问题的人,我目前的情况如下。。。要将声明永久存储在数据库中并在登录时自动加载,必须使用以下命令,并且更改在下次登录之前不会生效:

await userManager.AddClaimAsync(user, new Claim("your-claim", "your-value"));
使用以下命令只会存储当前会话的声明,因此我的做法是正确的:

((ClaimsIdentity)User.Identity).AddClaim(new Claim("your-claim", "your-value"));
我发现,要在控制器操作之间“粘住”声明,唯一的方法是重写UserClaimsPrincipalFactory,以便ASP.Net在登录过程中使用自定义代码。这里的问题是,您只能通过相关方法访问ApplicationUser,并且不能真正地将任何自定义参数传递给AFAIK。所以我首先用ApplicationUser类修改如下:

public class ApplicationUser : IdentityUser
{
    public long ActiveDealershipID { get; set; }
} 
public class MyClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public MyClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> options) : base(userManager, roleManager, options)
    {
    }

    public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var principal = await base.CreateAsync(user);

        long dealershipID = user.ActiveDealershipID;
        ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("DealershipID", dealershipID.ToString()));

        return principal;
    }
}
(必须应用EF迁移)。现在,我创建了一个从UserClaimsPrincipalFactory派生的类,如下所示:

public class ApplicationUser : IdentityUser
{
    public long ActiveDealershipID { get; set; }
} 
public class MyClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public MyClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> options) : base(userManager, roleManager, options)
    {
    }

    public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var principal = await base.CreateAsync(user);

        long dealershipID = user.ActiveDealershipID;
        ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("DealershipID", dealershipID.ToString()));

        return principal;
    }
}
公共类MyClaimsPrincipalFactory:UserClaimsPrincipalFactory
{
公共MyClaimsPrincipalFactory(UserManager用户管理器,RoleManager角色管理器,IOOptions):基本(UserManager,RoleManager,options)
{
}
公共异步覆盖任务CreateAync(ApplicationUser用户)
{
var principal=await base.CreateAsync(用户);
long dealershipID=user.ActiveDealershipID;
((ClaimsIdentity)principal.Identity).AddClaim(新索赔(“DealershipID”,DealershipID.ToString());
返还本金;
}
}
(您必须在startup.cs ConfigureServices中注册服务):

services.addScope();
然后在我的登录操作中,我在登录之前从模型数据中设置新的user属性,以便UserClaimsPrincipalFactory在设置声明时可以使用它:

public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    ViewData["ReturnUrl"] = returnUrl;
    if (ModelState.IsValid)
    {
        ApplicationUser user = await _userManager.FindByEmailAsync(model.Email);
        user.ActiveDealershipID = model.ChosenDealership
        await _userManager.UpdateAsync(user);

        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            ...
public异步任务登录(LoginViewModel模型,字符串returnUrl=null)
{
ViewData[“ReturnUrl”]=ReturnUrl;
if(ModelState.IsValid)
{
ApplicationUser=wait\u userManager.findbyemailsync(model.Email);
user.ActiveDealershipID=model.ChosenDealership
wait_userManager.UpdateAsync(用户);
var result=wait _signInManager.PasswordSignInAsync(model.Email、model.Password、model.RememberMe、lockoutOnFailure:false);
if(result.successed)
{
_logger.LogInformation(“用户登录”);
...

我仍然愿意接受更好的建议或其他想法,但这似乎对我有效。

如果用户只有一家公司,那将是一种方法,但在您的情况下不起作用,因为用户可以在每次会话中动态更改公司。@Evk-这就是我设置用户属性并调用
userManager.updatesync(user)
I的原因