使用 OWIN 和 ASP.NET WEB API 的基本身份驗證中間件
我創建了一個 ASP.NET WEB API 2.2 項目。我為 Visual Studio 中可用的個人帳戶使用了基於 Windows Identity Foundation 的模板,請參見此處。
Web 客戶端(用 angularJS 編寫)使用帶有 Web 瀏覽器 cookie 的 OAUTH 實現來儲存令牌和刷新令牌。我們受益於有用的UserManager和RoleManager類來管理使用者及其角色。OAUTH 和 Web 瀏覽器客戶端一切正常。
但是,對於基於桌面的客戶端的一些追溯兼容性問題,我還需要支持基本身份驗證。理想情況下,我希望*$$ Authorize $$,$$ Authorize(Role = “administrators”) $$*等屬性與 OAUTH 和基本身份驗證方案一起使用。
因此,按照LeastPrivilege中的程式碼,我創建了一個繼承自AuthenticationMiddleware的 OWIN BasicAuthenticationMiddleware。我來到了以下實現。對於BasicAuthenticationMiddleWare,只有 Handler 與 Leastprivilege 的程式碼相比發生了變化。實際上我們使用 ClaimsIdentity 而不是一系列Claim。
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); }但是,即使在 Handler 的AuthenticationAsyncCore方法中設置*Request.User**$$ Authorize $$*屬性無法按預期工作:每次我嘗試使用基本身份驗證方案時,都會以錯誤 401 未經授權的方式響應。關於出了什麼問題的任何想法?
我找到了罪魁禍首,在 WebApiConfig.cs 文件中,“個人使用者”模板插入了以下幾行。
//// Web API configuration and services //// Configure Web API to use only bearer token authentication. config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));因此我們還必須註冊我們的BasicAuthenticationMiddleware
config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); config.Filters.Add(new HostAuthenticationFilter(BasicAuthenticationOptions.BasicAuthenticationType));其中 BasicAuthenticationType 是傳遞給BasicAuthenticationOptions的基本建構子的常量字元串“Basic”
public class BasicAuthenticationOptions : AuthenticationOptions { public const string BasicAuthenticationType = "Basic"; public BasicAuthenticationMiddleware.CredentialValidationFunction CredentialValidationFunction { get; private set; } public BasicAuthenticationOptions( BasicAuthenticationMiddleware.CredentialValidationFunction validationFunction) : base(BasicAuthenticationType) { CredentialValidationFunction = validationFunction; } }