c# - tag - razor mvc
¿Cómo creo un proveedor de membresía personalizado para ASP.NET MVC 2? (4)
¿Cómo creo una membresía personalizada para ASP.NET MVC 2 basada en el proveedor de membresía de ASP.NET?
También es posible utilizar esto con una cantidad mucho menor de código, no estoy del todo seguro de si este método es tan seguro, pero funciona muy bien con cualquier base de datos que utilice.
en el 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);
}
}
}
}
lo que hace es que lee los roles de authCookie que se hizo desde FormsAuthenticationTicket
y la lógica de inicio de sesión se ve así
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);
}
}
}
Guardo los roles en mi base de datos con dos tablas: tabla: Rol que tiene las columnas: roleID y roleName y la tabla: UsersRoles que tiene las columnas: userID e roleID, esto hace posible múltiples roles para varios usuarios y es fácil crea tu propia lógica para agregar / eliminar roles de los usuarios, etc. Esto le permite usar [Authorize (Roles = "Super Admin")] por ejemplo. espero que esto ayude.
editar: se olvidó de hacer la verificación de la contraseña, pero solo agrega un si en el método logOn que verifica si el nombre de usuario y la contraseña provistos verifican y si no, devuelve falso
Utilicé el código fuente del proveedor NauckIt.PostgreSQL como base y lo modifiqué para satisfacer mis necesidades.
Creé un nuevo proyecto que contiene un proveedor de membresía personalizado y anulé el método ValidateUser
de la clase abstracta MembershipProvider
:
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);
}
}
Luego conecté ese proveedor a mi proyecto ASP.NET MVC 2 agregando una referencia y señalándolo desde mi web.config:
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider"
applicationName="MyApp"
Description="My Membership Provider"
passwordFormat="Clear"
connectionStringName="MyMembershipConnection"
type="MyApp.MyMembershipProvider" />
</providers>
</membership>
Necesito crear una clase personalizada que herede la clase abstracta RoleProvider
y anule el método GetRolesForUser
. La autorización MVC de ASP.NET utiliza ese método para averiguar qué roles se asignan al usuario que está conectado y se asegura de que el usuario tenga acceso a la acción del controlador.
Estos son los pasos que debemos seguir:
1) Cree una clase personalizada que herede la clase abstracta RoleProvider y anule el método GetRolesForUser:
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) Conecte el proveedor de roles con la aplicación ASP.NET MVC 2 a través de nuestro web.config:
<system.web>
...
<roleManager enabled="true" defaultProvider="MyRoleProvider">
<providers>
<clear />
<add name="MyRoleProvider"
applicationName="MyApp"
type="MyApp.MyRoleProvider"
connectionStringName="MyMembershipConnection" />
</providers>
</roleManager>
...
</system.web>
3) Establezca el Autorizador (Roles = "xxx, yyy") sobre el Controlador / Acción deseado:
[Authorization(Roles = "Customer Manager,Content Editor")]
public class MyController : Controller
{
......
}
¡Eso es! ¡Ahora funciona!
4) Opcional: establezca un atributo de Authorize
personalizado para que podamos redireccionar una función no deseada a una Página de AccesoDeniado:
[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 */);
}
}
Ahora podemos usar nuestro propio atributo make para redireccionar a nuestros usuarios para acceder a la vista denegada:
[MyAuthorization(Roles = "Portal Manager,Content Editor", ViewName = "AccessDenied")]
public class DropboxController : Controller
{
.......
}
¡Eso es! ¡Super duper!
Estos son algunos de los enlaces que he usado para obtener toda esta información:
Proveedor de rol personalizado: http://davidhayden.com/blog/dave/archive/2007/10/17/CreateCustomRoleProviderASPNETRolePermissionsSecurity.aspx
¡Espero que esta información ayude!