Wie erstelle ich eine benutzerdefinierte Mitgliedschaft für ASP.NET MVC 2 basierend auf dem ASP.NET-Mitgliedschaftsanbieter?
Ich habe ein neues Projekt erstellt, das einen benutzerdefinierten Mitgliedschaftsanbieter enthält, und die Methode ValidateUser
aus der abstrakten Klasse MembershipProvider
überschrieben:
public class MyMembershipProvider : MembershipProvider
{
public override bool ValidateUser(string username, string password)
{
// this is where you should validate your user credentials against your database.
// I've made an extra class so i can send more parameters
// (in this case it's the CurrentTerritoryID parameter which I used as
// one of the MyMembershipProvider class properties).
var oUserProvider = new MyUserProvider();
return oUserProvider.ValidateUser(username,password,CurrentTerritoryID);
}
}
Dann habe ich diesen Anbieter mit meinem ASP.NET MVC 2-Projekt verbunden, indem ich einen Verweis hinzugefügt und in meiner web.config darauf hingewiesen habe:
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider"
applicationName="MyApp"
Description="My Membership Provider"
passwordFormat="Clear"
connectionStringName="MyMembershipConnection"
type="MyApp.MyMembershipProvider" />
</providers>
</membership>
Ich muss eine benutzerdefinierte Klasse erstellen, die die abstrakte Klasse RoleProvider
erbt und die Methode GetRolesForUser
überschreibt. Die ASP.NET MVC-Autorisierung ermittelt anhand dieser Methode, welche Rollen dem aktuell angemeldeten Benutzer zugewiesen sind, und stellt sicher, dass der Benutzer auf die Controller-Aktion zugreifen darf.
Hier sind die Schritte, die wir unternehmen müssen:
1) Erstellen Sie eine benutzerdefinierte Klasse, die die abstrakte Klasse RoleProvider erbt und die Methode GetRolesForUser überschreibt:
public override string[] GetRolesForUser(string username)
{
SpHelper db = new SpHelper();
DataTable roleNames = null;
try
{
// get roles for this user from DB...
roleNames = db.ExecuteDataset(ConnectionManager.ConStr,
"sp_GetUserRoles",
new MySqlParameter("_userName", username)).Tables[0];
}
catch (Exception ex)
{
throw ex;
}
string[] roles = new string[roleNames.Rows.Count];
int counter = 0;
foreach (DataRow row in roleNames.Rows)
{
roles[counter] = row["Role_Name"].ToString();
counter++;
}
return roles;
}
2) Verbinden Sie den Rollenanbieter über unsere web.config mit der ASP.NET MVC 2-Anwendung:
<system.web>
...
<roleManager enabled="true" defaultProvider="MyRoleProvider">
<providers>
<clear />
<add name="MyRoleProvider"
applicationName="MyApp"
type="MyApp.MyRoleProvider"
connectionStringName="MyMembershipConnection" />
</providers>
</roleManager>
...
</system.web>
3) Setzen Sie die Berechtigung (Rollen = "xxx, yyy") über den gewünschten Controller/Aktion:
[Authorization(Roles = "Customer Manager,Content Editor")]
public class MyController : Controller
{
......
}
Das ist es! Jetzt funktioniert es!
4) Optional: Legen Sie ein benutzerdefiniertes Authorize
-Attribut fest, damit wir eine unerwünschte Rolle auf eine AccessDenied-Seite umleiten können:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class MyAuthorizationAttribute : AuthorizeAttribute
{
/// <summary>
/// The name of the master page or view to use when rendering the view on authorization failure. Default
/// is null, indicating to use the master page of the specified view.
/// </summary>
public virtual string MasterName { get; set; }
/// <summary>
/// The name of the view to render on authorization failure. Default is "Error".
/// </summary>
public virtual string ViewName { get; set; }
public MyAuthorizationAttribute ()
: base()
{
this.ViewName = "Error";
}
protected void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (AuthorizeCore(filterContext.HttpContext))
{
SetCachePolicy(filterContext);
}
else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// auth failed, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
}
else if (filterContext.HttpContext.User.IsInRole("SuperUser"))
{
// is authenticated and is in the SuperUser role
SetCachePolicy(filterContext);
}
else
{
ViewDataDictionary viewData = new ViewDataDictionary();
viewData.Add("Message", "You do not have sufficient privileges for this operation.");
filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData };
}
}
protected void SetCachePolicy(AuthorizationContext filterContext)
{
// ** IMPORTANT **
// Since we're performing authorization at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether a page should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
}
Jetzt können wir unser eigenes Attribut verwenden, um unsere Benutzer zum Zugriff auf die verweigerte Ansicht umzuleiten:
[MyAuthorization(Roles = "Portal Manager,Content Editor", ViewName = "AccessDenied")]
public class DropboxController : Controller
{
.......
}
Das ist es! Super duper!
Hier sind einige der Links, über die ich all diese Informationen abgerufen habe:
Benutzerdefinierter Rollenanbieter: http://davidhayden.com/blog/dave/archive/2007/10/17/CreateCustomRoleProviderASPNETRolePermissionsSecurity.aspx
Ich hoffe diese Info hilft!
Es ist auch möglich, dies mit einer viel geringeren Menge an Code zu verwenden. Ich bin nicht ganz sicher, ob diese Methode so sicher ist, aber sie funktioniert sehr gut mit jeder von Ihnen verwendeten Datenbank.
in der global.asax
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id =
(FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
// Get the stored user-data, in this case, our roles
string userData = ticket.UserData;
string[] roles = userData.Split(',');
HttpContext.Current.User = new GenericPrincipal(id, roles);
}
}
}
}
das bedeutet, dass die Rollen aus dem AuthCookie gelesen werden, der mit FormsAuthenticationTicket erstellt wurde
und die Anmeldelogik sieht so aus
public class dbService
{
private databaseDataContext db = new databaseDataContext();
public IQueryable<vwPostsInfo> AllPostsAndDetails()
{
return db.vwPostsInfos;
}
public IQueryable<role> GetUserRoles(int userID)
{
return (from r in db.roles
join ur in db.UsersRoles on r.rolesID equals ur.rolesID
where ur.userID == userID
select r);
}
public IEnumerable<user> GetUserId(string userName)
{
return db.users.Where(u => u.username.ToLower() == userName.ToLower());
}
public bool logOn(string username, string password)
{
try
{
var userID = GetUserId(username);
var rolesIQueryable = GetUserRoles(Convert.ToInt32(userID.Select(x => x.userID).Single()));
string roles = "";
foreach (var role in rolesIQueryable)
{
roles += role.rolesName + ",";
}
roles.Substring(0, roles.Length - 2);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // Ticket version
username, // Username associated with ticket
DateTime.Now, // Date/time issued
DateTime.Now.AddMinutes(30), // Date/time to expire
true, // "true" for a persistent user cookie
roles, // User-data, in this case the roles
FormsAuthentication.FormsCookiePath);// Path cookie valid for
// Encrypt the cookie using the machine key for secure transport
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName, // Name of auth cookie
hash); // Hashed ticket
// Set the cookie's expiration time to the tickets expiration time
if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
// Add the cookie to the list for outgoing response
HttpContext.Current.Response.Cookies.Add(cookie);
return true;
}
catch
{
return (false);
}
}
}
ich speichere die Rollen in meiner Datenbank mit zwei Tabellen: Tabelle: Rolle, die die Spalten hat: Rollen-ID und Rollenname und die Tabelle: BenutzerRollen, die die Spalten hat: Benutzer-ID und Rollen-ID, dies ermöglicht mehrere Rollen für mehrere Benutzer und es ist einfach zu Erstellen Sie Ihre eigene Logik zum Hinzufügen/Entfernen von Rollen zu Benutzern usw. Auf diese Weise können Sie beispielsweise [Authorize (Roles = "Super Admin")] verwenden. hoffe das hilft.
edit: habe vergessen, das Passwort zu überprüfen, aber du fügst einfach ein if in die logOn-Methode ein, das prüft, ob der angegebene Benutzername und das angegebene Passwort geprüft werden und wenn nicht, wird false zurückgegeben
Ich habe den Quellcode des NauckIt.PostgreSQL-Providers als Basis verwendet und an meine Bedürfnisse angepasst.