注入的IPrincipal是匿名的,但Web API控制器用户对象已通过身份验证

本文关键字:控制器 API 用户 已通过 身份验证 Web 对象 IPrincipal 注入 | 更新日期: 2023-09-27 17:59:07

我正在编写一个Web API 2/MVC5项目,我想对一些必须使用ASP.Net Identity与IPrincipal一起工作的代码进行单元测试。我不想依赖IPrincipal,而是想把它抽象到我自己的IUserService后面。当我查看注入的IUserService时,UserIdUserName为空。

public interface IUserService
{
    string UserId { get;  }
    string UserName { get;  }
}

正在使用的具体实现是:

public class UserService : IUserService
{
    private IPrincipal _principal;
    public UserService(IPrincipal principal)
    {
        _principal = principal;
    }
    public string UserName
    {
        get { return _principal.Identity.GetUserName(); }
    }
    public string UserId
    {
        get { return _principal.Identity.GetUserId(); }
    }
}

这是使用Ninject进行依赖项注入。在NinjectWebCommon.cs内部我有:

private static void RegisterServices(IKernel kernel)
{
   kernel.Bind<IBooksService>().To<BooksService>().InRequestScope();
   kernel.Bind<DbContext>().To<ApplicationDbContext>().InRequestScope();
   kernel.Bind<ApplicationDbContext>().To<ApplicationDbContext>().InRequestScope();
   kernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>().InRequestScope();
   kernel.Bind<UserManager<ApplicationUser>>().To<UserManager<ApplicationUser>>().InRequestScope();
   kernel.Bind<IBookRepository>().To<BookRepository>().InRequestScope();
   kernel.Bind<IUserService>().To<UserService>().InRequestScope();
   kernel.Bind<IPrincipal>().ToMethod(ctx => HttpContext.Current.User);
}

如果我创建一个Func<IPrincipal>并传递()=>HttpContext.Current.User,一切都会正常工作。然而,我认为没有人需要这样做,所有的例子都表明了这种实现。

注入的IPrincipal是匿名的,但Web API控制器用户对象已通过身份验证

您是否对用户进行过身份验证?一旦用户通过了身份验证,就需要负责创建一个IIdentity和一个IPrincipal。然后,您需要用IPrincipal设置Thread.CurrentPrincipal,还需要将IPrincipal放在当前的HttpContext中。

为了使GenericIdentity不被视为匿名,Name属性必须是非空字符串。为了使ClaimsIdentity不被视为匿名,AuthenticationType属性必须是非null、非空字符串。

因此,通常情况下,您将执行以下操作:

// Perform authentication, usually using some kind of AuthenticationFilter or
// AuthorizationFilter.
// After authenticating, and still within the Auth*Filter,
// I'm going to use a GenericIdentity, but this can be converted to a 
// ClaimsIdentity if you're using the default Name claim.
IIdentity identity = new GenericIdentity("myUserName", "myAuthenticationType");
// Again, you could use a ClaimsPrincipal, the concept, however, is the same.
IPrincipal principal = new GenericPrincipal(identity, null /* no roles */);
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = principal;

我确实看到你提到了正在使用的新的ASP.Net标识模型。因此,您肯定会在代码中使用ClaimsIdentityClaimsPrincipal

这只是我的猜测,但这可能是错误的:

kernel.Bind<IUserService>().To<UserService>().InRequestScope();

IUserService的首次使用可能发生在设置HttpContext.Current.User之前,因此您得到的不是当前用户,而是null,因为在创建UserService时,HttpContext.Current.Usernull。由于已经定义了InRequestScope(),所以在后续使用中会提供UserService的第一个创建对象,所以主体始终为null。

我可能会直接使用HttpContext

public class HttpContextBasedUserService : IUserService
{
    public string UserName
    {
        get { return HttpContext.Current.User.Identity.GetUserName(); }
    }
    public string UserId
    {
        get { return HttpContext.Current.User.Identity.GetUserId(); }
    }
}

如果您想在桌面应用程序中使用IUserService,请创建专用于桌面使用的实现。