Controller.User由ASP.NET MVC 5上的ASP.NET标识不一致地设置

Gup*_*R4c 5 c# asp.net-mvc owin asp.net-mvc-5 asp.net-identity

我正在尝试将我的身份验证与控制器分离,因此我使用Ninject AuthenticationService将我注入到身份验证控制器(DefaultController)中.这是实施AuthenticationService:

public sealed class AuthenticationService {
    private IAuthenticationManager AuthenticationManager { get; set; }
    private UserManager<Employee, int> EmployeeManager { get; set; }

    public AuthenticationService(
        IAuthenticationManager authenticationManager,
        UserManager<Employee, int> employeeManager) {
        this.AuthenticationManager = authenticationManager;
        this.EmployeeManager = employeeManager;
    }

    public bool SignIn(
        CredentialsInput credentials) {
        Employee employee = this.EmployeeManager.Find(credentials.Email, credentials.Password);

        if (employee != null) {
            ClaimsIdentity identityClaim = this.EmployeeManager.CreateIdentity(employee, DefaultAuthenticationTypes.ApplicationCookie);

            if (identityClaim != null) {
                this.AuthenticationManager.SignIn(new AuthenticationProperties(), identityClaim);

                return true;
            }
        }

        return false;
    }

    public void SignOut() {
        this.AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是我如何配置Ninject进行注射:

private static void RegisterServices(
    IKernel kernel) {
    kernel.Bind<IUserStore<Employee, int>>().To<EmployeeStore>().InRequestScope();
    kernel.Bind<IRoleStore<Role, int>>().To<RoleStore>().InRequestScope();
    kernel.Bind<IAuthenticationManager>().ToMethod(
        c =>
            HttpContext.Current.GetOwinContext().Authentication).InRequestScope();
}
Run Code Online (Sandbox Code Playgroud)

这是我如何配置OWIN:

public sealed class StartupConfig {
    public void Configuration(
        IAppBuilder app) {
        this.ConfigureAuthentication(app);
    }

    public void ConfigureAuthentication(
        IAppBuilder app) {
        app.UseCookieAuthentication(new CookieAuthenticationOptions {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/"),
            ExpireTimeSpan = new TimeSpan(0, 60, 0)
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

哦,这里的好处是DefaultController:

public sealed class DefaultController : Controller {
    private AuthenticationService AuthenticationService { get; set; }

    public DefaultController(
        AuthenticationService authenticationService) {
        this.AuthenticationService = authenticationService;
    }

    [HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
    public RedirectToRouteResult Default(
        [Bind(Prefix = "Credentials", Include = "Email,Password")] CredentialsInput credentials) {
        if (base.ModelState.IsValid
            && this.AuthenticationService.SignIn(credentials)) {
            //  Place of error. The IsInRole() method doesn't return
            //  the correct answer because the
            //  IUserStore<>.FindByIdAsync() method is not setting
            //  the correct data.
            if (this.User.IsInRole("Technician")) {
                return this.RedirectToAction<TechniciansController>(
                    c =>
                        c.Default());
            }

            return this.RedirectToAction(
                c =>
                    c.Dashboard());
        }

        return this.RedirectToAction(
            c =>
                c.Default());
    }

    [HttpGet]
    public RedirectToRouteResult SignOut() {
        this.AuthenticationService.SignOut();

        return this.RedirectToAction(
            c =>
                c.Default());
    }
}
Run Code Online (Sandbox Code Playgroud)

好吧,这一切都有效.我遇到的问题是由它设置的会话UserManager是不一致的.例如,如果我构建应用程序然后运行它(调试与否),则会发生以下情况:

  • 建立并运行
  • 以user1@email.com身份登录
  • 登出
  • 以user2@email.com身份登录
  • 在第一个帖子请求中,user1仍然显示为身份
  • 在第二个发布请求(刷新)上,user2正确显示为标识

有人可以指出为什么会这样吗?当然,我确实把控制器中的代码SignInSignOut控制器中的代码拉出来了AuthenticationService,但是我并不完全相信这是导致不一致的原因.由于所有的程序集都是由MVC应用程序处理的,它应该都是一样的,对吧?非常感谢帮助.

UPDATE

谷歌搜索时找到了这个,虽然它标记为已解决,但我对解决方案的内容有点困惑.https://katanaproject.codeplex.com/workitem/201我不认为这与我的问题有任何关系.

更新2

我相信问题的根源是在调用管道中的某个地方异步,特别是在EmployeeStore我注入的内容中UserManager<Employee, int>.我有一个被UserStore调用的自定义实现EmployeeStore.它实现了IQueryableUserStore<Employee, int>,IUserStore<Employee, int>,IUserPasswordStore<Employee, int>,和IUserRoleStore<Employee, int>.

当我调试FindByIdAsync(int employeeId)方法时,我发现由于某种原因它会触发两次.它第一次发射我看到正确的employeeId传入它.第二次触发它employeeId被设置为0.这是它搞砸的地方,我认为因为它随后不会调用该IsInRoleAsync(Employee employee, string roleName)方法.

当我在抛出一个未找到UserId的异常后刷新页面时,该FindByIdAsync(...)方法再次被调用两次,但这次两次employeeId都是正确的,然后它继续调用该IsInRoleAsync(...)方法.

这是两种方法的代码:

public Task<Employee> FindByIdAsync(
    int employeeId) {
    this.ThrowIfDisposed();

    return this.Repository.FindSingleOrDefaultAsync<Employee, int>(employeeId);
}

public Task<bool> IsInRoleAsync(
    Employee employee,
    string roleName) {
    this.ThrowIfDisposed();

    if (employee == null) {
        throw new ArgumentNullException("employee");
    }

    if (String.IsNullOrEmpty(roleName)) {
        throw new ArgumentNullException("roleName");
    }

    return Task.FromResult<bool>(employee.Roles.Any(
        r =>
            (r.Name == roleName)));
}
Run Code Online (Sandbox Code Playgroud)

Gup*_*R4c 0

好吧,我没有真正的解决方案,但我有一个可行的解决方案。由于User在登录过程中未设置 ,因此我重定向到另一个名为 的操作,DefaultRedirect我现在可以完全访问该User属性,并且可以从那里重定向我真正想要的方式。这是一个肮脏的作弊行为,但它不是一个真正的解决方案。我仍然认为 ASP.NET Identity 在某种程度上无法正常工作。无论如何,这是我的作弊:

    [HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
    public RedirectToRouteResult Default(
        [Bind(Prefix = "Credentials", Include = "Email,Password")] CredentialsInput credentials) {
        if (base.ModelState.IsValid
            && this.AuthenticationService.SignIn(credentials)) {
            return this.RedirectToAction(
                c =>
                    c.DefaultRedirect());
        }

        return this.RedirectToAction(
            c =>
                c.Default());
    }

    [HttpGet]
    public RedirectToRouteResult DefaultRedirect() {
        if (this.User.IsInRole("Technician")) {
            return this.RedirectToAction<TechniciansController>(
                c =>
                    c.Default());
        }

        return this.RedirectToAction(
            c =>
                c.Dashboard());
    }
Run Code Online (Sandbox Code Playgroud)

如果有人知道的话,我仍在寻找真正的解决方案!