我正在尝试实现一个非常简单的 FormsAuthentication 示例。这不是现实生活,但它引发了一个问题。旨在成为应用程序级别单例的 AuthenticationService 似乎被实例化了两次。
这是代码:
public class User : IUserIdentity
{
public string UserName { get; set; }
public IEnumerable<string> Claims { get; set; }
}
public interface IAuthenticationService
{
Guid GetIdentifier(string username, string password);
}
public class AuthenticationService : IUserMapper, IAuthenticationService
{
public readonly Guid Identifier = Guid.NewGuid();
private readonly string Username = "admin";
private readonly string Password = "x";
public Guid GetIdentifier(string username, string password)
{
return (username == Username && password == Password) ? Identifier : Guid.Empty;
}
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{
return (identifier == Identifier) ? new User { UserName = "admin" } : null;
}
}
public class MyBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
container.Register<IAuthenticationService, AuthenticationService>().AsSingleton();
}
}
我正在使用上面的代码,LoginModule
如下所示。请注意,我正在AuthenticationService
通过模块的构造函数注入应用程序级单例实例。
public LoginModule(IAuthenticationService authenticationService)
{
Post["/login"] = _ =>
{
var identifier = authenticationService.GetIdentifier(
(string) Form.Username,
(string) Form.Password);
if (identifier.IsEmpty())
{
return Context.GetRedirect("~/login?error=true");
}
return this.LoginAndRedirect(identifier);
};
}
What should happen is that when the user POSTs
their username and password, these are checked by the AuthenticationService
via the GetIdentifier(..)
method. If the credentials match then the single GUID
identifier is returned. This GUID
will always be the same because it is created as a readonly
field and thus set once when the singleton AuthenticationService
is first instantiated at application startup.
However this is not the case. Instead two distinct instances of the AuthenticationService
are created, one that is injected into the LoginModule
constructor and used to call the GetIdentifier(..)
method and another instance which Nancy uses to call the IUserIdentity.GetUserFromIdentifier(..)
method.
These two instances have different GUID
identifiers and so the GetUserFromIdentifier(..)
method always return null.
I have tested a standard singleton service that does not implement IUserMapper
and it works as expected, only one instance is created.
So it seems that Nancy is instantiating the IUserMapper
singleton twice, once for its own internal use during FormsAuthentication, and once to inject into my LoginModule
constructor!
Can you spot my mistake?
Thanks