Tôi đã trải qua khá nhiều cùng một kịch bản quá khứ vài tuần vì vậy đây có thể giúp đỡ người khác trong cùng một thuyền. Kịch bản của tôi là một ứng dụng MVC4 trên mạng intranet của công ty với người dùng được lưu trữ trong Active Directory. Điều này cho phép xác thực Windows cho đăng nhập một lần, do đó không cần xác thực Mẫu. Vai trò được lưu trữ trong cơ sở dữ liệu Oracle. Tôi có 3 vai trò:
- chỉ đọc: Tất cả người dùng cần phải là một thành viên của này để truy cập ứng dụng
- User: Tạo resords mới
- Admin: Chỉnh sửa và xóa các bản ghi
tôi quyết định sử dụng api nhà cung cấp vai trò asp.net để tạo AccountRoleProvider của riêng tôi. Cho đến nay tôi chỉ cần sử dụng 2 phương pháp trong này, GetRolesForUser và IsUserInRole:
public class AccountRoleProvider : RoleProvider // System.Web.Security.RoleProvider
{
private readonly IAccountRepository _accountRepository;
public AccountRoleProvider(IAccountRepository accountRepository)
{
this._accountRepository = accountRepository;
}
public AccountRoleProvider() : this (new AccountRepository())
{}
public override string[] GetRolesForUser(string user521)
{
var userRoles = this._accountRepository.GetRoles(user521).ToArray();
return userRoles;
}
public override bool IsUserInRole(string username, string roleName)
{
var userRoles = this.GetRolesForUser(username);
return Utils.IndexOfString(userRoles, roleName) >= 0;
}
}
tôi cập nhật web.config để sử dụng nhà cung cấp vai trò của tôi:
<authentication mode="Windows" />
<roleManager enabled="true" defaultProvider="AccountRoleProvider">
<providers>
<clear/>
<add name="AccountRoleProvider"
type="MyApp.Infrastructure.AccountRoleProvider" />
</providers>
</roleManager>
Sau đó, tôi tạo ra 2 tùy chỉnh các thuộc tính từ AuthorizeAttribute , ReadOnlyAuthorize và CustomAuthorize.
ReadonlyAuthorize:
public class ReadonlyAuthorize : AuthorizeAttribute
{
private IAccountRepository _accountRepository;
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = httpContext.User;
this._accountRepository = new AccountRepository();
if (!user.Identity.IsAuthenticated)
{
return false;
}
// Get roles for current user
var roles = this._accountRepository.GetRoles(user.Identity.Name);
if (!roles.Contains("readonly"))
{
return false;
}
return base.AuthorizeCore(httpContext);
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
}
}
}
CustomAuthorize:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public string RedirectActionName { get; set; }
public string RedirectControllerName { get; set; }
private IAccountRepository _accountRepository;
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = httpContext.User;
this._accountRepository = new AccountRepository();
var accessAllowed = false;
// Get the roles passed in with the (Roles = "...") on the attribute
var allowedRoles = this.Roles.Split(',');
if (!user.Identity.IsAuthenticated)
{
return false;
}
// Get roles for current user
var roles = this._accountRepository.GetRoles(user.Identity.Name);
foreach (var allowedRole in allowedRoles)
{
if (roles.Contains(allowedRole))
{
accessAllowed = true;
}
}
if (!accessAllowed)
{
return false;
}
return base.AuthorizeCore(httpContext);
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
{
var values = new RouteValueDictionary(new
{
action = this.RedirectActionName == string.Empty ? "AccessDenied" : this.RedirectActionName,
controller = this.RedirectControllerName == string.Empty ? "Home" : this.RedirectControllerName
});
filterContext.Result = new RedirectToRouteResult(values);
}
}
}
Lý do cho 2 thuộc tính khác nhau là tôi sử dụng một cho vai trò chỉ đọc mà tất cả người dùng phải là thành viên của để truy cập ứng dụng. Tôi có thể thêm này trong phương pháp RegisterGlobalFilters trong Global.asax có nghĩa là nó được áp dụng tự động cho tất cả các Bộ điều khiển:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new ReadonlyAuthorize());
}
Sau đó, trong CustomAuthorize tôi có thể tham gia một cách tiếp cận chi tiết hơn và xác định vai trò mà tôi muốn và áp dụng đối với một Bộ điều khiển hoặc một hành động cá nhân dưới đây, tôi có thể giới hạn truy cập vào các phương pháp Delete để người sử dụng trong vai trò quản lý:
[AccessDeniedAuthorize(RedirectActionName = "AccessDenied", RedirectControllerName = "Home", Roles = "Admin")]
public ActionResult Delete(int id = 0)
{
var batch = myDBContext.Batches.Find(id);
if (batch == null)
{
return HttpNotFound();
}
return View(batch);
}
Có những bước xa hơn tôi cần phải thực hiện như cập nhật các đối tượng người dùng với vai trò người sử dụng hiện nay là một thành viên của. Điều này sẽ lấy các vai trò cho Người dùng một lần thay vì mỗi lần trong các thuộc tính tùy chỉnh của tôi và cũng sử dụng User.IsInRole. Một cái gì đó như thế này nên có thể trong Application_AuthenticateRequest trong Gloal.asax:
var roles = "get roles for this user from respository";
if (Context.User != null)
Context.User = new GenericPrincipal(Context.User.Identity, roles);