Я создал проект ASP.NET WEB API 2.2. Я использовал шаблон, основанный на Windows Identity Foundation, для отдельных учетных записей, доступных в visual studio см. Здесь.
Веб-клиент (написанный в angularJS) использует реализацию OAUTH с куки файлами веб-браузера для хранения токена и токена обновления. Мы используем полезные классы UserManager и RoleManager для управления пользователями и их ролями. Все отлично работает с OAUTH и клиентом веб-браузера.
Однако, для некоторых проблем ретро-совместимости с клиентами на базе настольных компьютеров мне также необходимо поддерживать обычную проверку подлинности. В идеале я хотел бы, чтобы атрибуты [Authorize], [Authorize (Role = "administrators" )] и т.д. Работали как с OAUTH, так и с базовой схемой аутентификации.
Таким образом, следуя коду LeastPrivilege, я создал OWIN BasicAuthenticationMiddleware, который наследуется от AuthenticationMiddleware. Я пришел к следующей реализации. Для BasicAuthenticationMiddleWare только обработчик изменился по сравнению с кодом Leastprivilege. На самом деле мы используем ClaimsIdentity, а не ряд требований.
class BasicAuthenticationHandler: AuthenticationHandler<BasicAuthenticationOptions>
{
private readonly string _challenge;
public BasicAuthenticationHandler(BasicAuthenticationOptions options)
{
_challenge = "Basic realm=" + options.Realm;
}
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
var authzValue = Request.Headers.Get("Authorization");
if (string.IsNullOrEmpty(authzValue) || !authzValue.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
{
return null;
}
var token = authzValue.Substring("Basic ".Length).Trim();
var claimsIdentity = await TryGetPrincipalFromBasicCredentials(token, Options.CredentialValidationFunction);
if (claimsIdentity == null)
{
return null;
}
else
{
Request.User = new ClaimsPrincipal(claimsIdentity);
return new AuthenticationTicket(claimsIdentity, new AuthenticationProperties());
}
}
protected override Task ApplyResponseChallengeAsync()
{
if (Response.StatusCode == 401)
{
var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
if (challenge != null)
{
Response.Headers.AppendValues("WWW-Authenticate", _challenge);
}
}
return Task.FromResult<object>(null);
}
async Task<ClaimsIdentity> TryGetPrincipalFromBasicCredentials(string credentials,
BasicAuthenticationMiddleware.CredentialValidationFunction validate)
{
string pair;
try
{
pair = Encoding.UTF8.GetString(
Convert.FromBase64String(credentials));
}
catch (FormatException)
{
return null;
}
catch (ArgumentException)
{
return null;
}
var ix = pair.IndexOf(':');
if (ix == -1)
{
return null;
}
var username = pair.Substring(0, ix);
var pw = pair.Substring(ix + 1);
return await validate(username, pw);
}
Затем в Startup.Auth я объявляю следующий делегат для проверки подлинности (просто проверяет, существует ли пользователь и правильно ли пароль, и генерирует связанное с ним свойство ClaimsIdentity)
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(DbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
Func<string, string, Task<ClaimsIdentity>> validationCallback = (string userName, string password) =>
{
using (DbContext dbContext = new DbContext())
using(UserStore<ApplicationUser> userStore = new UserStore<ApplicationUser>(dbContext))
using(ApplicationUserManager userManager = new ApplicationUserManager(userStore))
{
var user = userManager.FindByName(userName);
if (user == null)
{
return null;
}
bool ok = userManager.CheckPassword(user, password);
if (!ok)
{
return null;
}
ClaimsIdentity claimsIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
return Task.FromResult(claimsIdentity);
}
};
var basicAuthOptions = new BasicAuthenticationOptions("KMailWebManager", new BasicAuthenticationMiddleware.CredentialValidationFunction(validationCallback));
app.UseBasicAuthentication(basicAuthOptions);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
//If the AccessTokenExpireTimeSpan is changed, also change the ExpiresUtc in the RefreshTokenProvider.cs.
AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
AllowInsecureHttp = true,
RefreshTokenProvider = new RefreshTokenProvider()
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
Однако даже с настройками Request.User в методе AuthenticationAsyncCore обработчика атрибут [Авторизовать] работает не так, как ожидалось: при ошибке с ошибкой 401 каждый раз, когда я пытаюсь использовать схему базовой проверки подлинности, неавторизируется. Любая идея о том, что происходит не так?