Asp.net

在 MVC5 中使用現有使用者數據庫進行複雜的身份驗證

  • August 28, 2014

我正在將 SaaS 應用程序從經典 ASP 遷移到 .NET MVC5,並將首先使用 EF6 數據庫。最終使用者的登錄表單可由每個租戶自定義(在他們自己的子域上但指向同一個 Web 應用程序)。我們希望使用現有的數據庫模式和新的身份驗證和授權過濾器。

例如,一個租戶的使用者可以通過輸入他們的名字、姓氏和我們系統生成的程式碼來登錄。另一個租戶的使用者可以通過輸入他們的電子郵件地址和密碼來登錄。此外,每個租戶都有一個單獨的管理員登錄名,該登錄名使用使用者名和密碼。另一個租戶可能對遠端 AD 伺服器使用 LDAP 身份驗證。

是否有明確的最佳實踐方式來進行自定義身份驗證?

幾乎每篇文章似乎都提出了不同的方法來實現這一點:簡單地設置FormsAuthentication.SetAuthCookie、使用自定義 OWIN 提供程序、覆蓋AuthorizeAttribute等。

在經典 ASP 中,我們查詢數據庫以找出該租戶的登錄類型,在登錄螢幕上顯示適當的欄位,然後在回發時,檢查欄位與數據庫中的內容匹配,然後適當地設置會話變數,它們是檢查每個頁面請求。

謝謝

我發現身份框架在身份驗證選項方面非常靈活。看看這段驗證碼:

var identity = await this.CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie);

authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);

這是身份驗證部分的標準執行,您可以在網路上的每個身份範例中找到它。如果你仔細觀察它是非常靈活的——你所需要的身份驗證是ApplicationUser框架並不關心你如何獲得的對象。

所以理論上你可以做這樣的事情(虛擬碼,我沒有嘗試編譯這個):

// get user object from the database with whatever conditions you like
// this can be AuthCode which was pre-set on the user object in the db-table
// or some other property
var user = dbContext.Users.Where(u => u.Username == "BillyJoe" && u.Tenant == "ExpensiveClient" && u.AuthCode == "654")

// check user for null 

// check if the password is correct - don't have to do that if you are doing
// super-custom auth.
var isCorrectPassword = await userManager.CheckPasswordAsync(user, "enteredPassword");

if (isCorrectPassword)
{
   // password is correct, time to login
   // this creates ClaimsIdentity object from the ApplicationUser object
   var identity = await this.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);

   // now we can set claims on the identity. Claims are stored in cookie and available without
   // querying database
   identity.AddClaim(new Claim("MyApp:TenantName", "ExpensiveClient"));
   identity.AddClaim(new Claim("MyApp:LoginType", "AuthCode"));
   identity.AddClaim(new Claim("MyApp:CanViewProducts", "true"));


   // this tells OWIN that it can set auth cookie when it is time to send 
   // a reply back to the client
   authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}

使用此身份驗證,您已經對使用者設置了一些聲明 - 它們儲存在 cookie 中,並且可以通過ClaimsPrincipal.Current.Claims. 聲明本質上是字元串鍵值對的集合,您可以在其中儲存任何您喜歡的內容。

我通常通過擴展方法訪問使用者的聲明:

public static String GetTenantName(this ClaimsPrincipal principal)
{
   var tenantClaim = principal.Claims.FirstOrDefault(c => c.Type == "MyApp:TenantName");
   if (tenantClaim != null)
   {
       return tenantClaim.Value;
   }

   throw new ApplicationException("Tenant name is not set. Can not proceed");
}

public static String CanViewProducts(this ClaimsPrincipal principal)
{
   var productClaim = principal.Claims.FirstOrDefault(c => c.Type == "MyApp:CanViewProducts");
   if (productClaim == null)
   {
       return false;
   }

   return productClaim.Value == "true";
}

因此,在您的控制器/視圖/業務層中,您可以隨時呼叫ClaimsPrincipal.Current.GetTenantName(),在這種情況下,您會得到“ExpensiveClient”。

或者,如果您需要檢查是否為使用者啟用了特定功能,您可以

if(ClaimsPrincipal.Current.CanViewProducts())
{
   // display products
}

如何儲存使用者屬性取決於您,但只要您將它們設置為 cookie 上的聲明,它們就可用。

或者,您可以為每個使用者將聲明添加到數據庫中:

await userManager.AddClaimAsync(user.Id, new Claim("MyApp:TenantName", "ExpensiveClient"));

這會將聲明保存到數據庫中。預設情況下,身份框架會在使用者登錄時將此聲明添加到使用者,而無需您手動添加。

但請注意,您不能對 cookie 設置太多聲明。Cookie 具有瀏覽器設置的 4K 限制。身份 cookie 加密的工作方式將編碼文本增加了大約 1.1,因此您可以擁有大約 3.6K 表示聲明的文本。我在這裡遇到了這個問題

更新

要通過聲明控制對控制器的訪問,您可以在控制器上使用以下過濾器:

public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
   public string Name { get; private set; }


   public ClaimsAuthorizeAttribute(string name)
   {
       Name = name;
   }

   public override void OnAuthorization(AuthorizationContext filterContext)
   {
       var user = HttpContext.Current.User as ClaimsPrincipal;
       if (user.HasClaim(Name, Name))
       {
           base.OnAuthorization(filterContext);
       }
       else
       {
           filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary()
           {
               {"controller", "errors"},
               {"action", "Unauthorised"}
           });
       }
   }
}

然後在控制器或單獨的操作上使用此屬性,如下所示:

   [ClaimsAuthorize("Creating Something")]
   public ActionResult CreateSomething()
   {
       return View();
   }

使用者需要“創建某物”聲明才能訪問此操作,否則他們將被重定向到“未經身份驗證”頁面。

最近我玩了聲明身份驗證,並製作了一個與您的要求類似的原型應用程序。請查看簡單版本:https ://github.com/trailmax/ClaimsAuthorisation/tree/SimpleClaims其中為每個使用者單獨儲存聲明。或者有更複雜的解決方案,其中聲明屬於一個角色,當使用者登錄時,角色聲明分配給使用者:https ://github.com/trailmax/ClaimsAuthorisation/tree/master

引用自:https://stackoverflow.com/questions/25054910