我在ASP.net MVC4中遇到自定义角色提供程序的问题。我实现了一个非常轻量级的RoleProvider,在我改变之前似乎工作得很好
[Authorize]
public class BlahController:....
}
到
[Authorize(Roles="Administrator")]
public class BlahController:....
}
一旦我进行了更改,用户就不再经过身份验证,我会收到401错误。这很奇怪,因为我的RoleProvider基本上为IsUSerInRole返回true,并且包含GetUserRoles的“Administrator”列表。我在自定义RoleProvider中的每个方法上都有断点,发现它们都没有被调用。
接下来,我实现了自己的authorize属性,该属性继承自AuthorizeAttribute。在这里,我提出了分数,所以我可以看到发生了什么。事实证明,由底层属性调用的User.IsInRole()返回false。
我确信角色提供程序已正确设置。我在配置文件中有这个
<roleManager enabled="true" defaultProvider="SimplicityRoleProvider">
<providers>
<clear />
<add name="SimplicityRoleProvider" type="Simplicity.Authentication.SimplicityRoleProvider" applicationName="Simplicity" />
</providers>
</roleManager>
并使用此处描述的方法检查哪个角色提供者是当前角色提供者:Reference current RoleProvider instance?产生正确的结果。但是User.IsInRole仍然存在返回false。
我正在使用Azure访问控制服务,但我看不出它与自定义角色提供程序不兼容。
如何更正IPrincipal用户,以便IsInRole从我的自定义RoleProvider返回值?
RoleProvider来源:
公共类SimplicityRoleProvider:RoleProvider { 私有ILog日志{get;组; }
public SimplicityRoleProvider()
{
log = LogManager.GetLogger("ff");
}
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
log.Warn(usernames);
log.Warn(roleNames);
}
public override string ApplicationName
{
get
{
return "Simplicity";
}
set
{
}
}
public override void CreateRole(string roleName)
{
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
return true;
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
log.Warn(roleName);
log.Warn(usernameToMatch);
return new string[0];
}
public override string[] GetAllRoles()
{
log.Warn("all roles");
return new string[0];
}
public override string[] GetRolesForUser(string username)
{
log.Warn(username);
return new String[] { "Administrator" };
}
public override string[] GetUsersInRole(string roleName)
{
log.Warn(roleName);
return new string[0];
}
public override bool IsUserInRole(string username, string roleName)
{
log.Warn(username);
log.Warn(roleName);
return true;
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
}
public override bool RoleExists(string roleName)
{
log.Warn(roleName);
return true;
}
}
答案 0 :(得分:2)
当您拥有自定义AuthorizeAttribute和自定义RoleProvider时,似乎没有自动连接System.Web.Security.Roles.GetRolesForUser(Username)。
因此,在自定义AuthorizeAttribute中,您需要从数据源中检索角色列表,然后将它们与作为参数传递给AuthorizeAttribute的角色进行比较。
我在一些博客文章中看到意味着手动比较角色是不必要的,但是当我们覆盖AuthorizeAttribute时,似乎我们正在压制这种行为并且需要自己提供它。
无论如何,我会介绍对我有用的东西。希望它会有所帮助。
我欢迎评论是否有更好的方法来实现这一目标。
请注意,在我的情况下,AuthorizeAttribute正在应用于ApiController,尽管我不确定这是一个相关的信息。
public class RequestHashAuthorizeAttribute : AuthorizeAttribute
{
bool requireSsl = true;
public bool RequireSsl
{
get { return requireSsl; }
set { requireSsl = value; }
}
bool requireAuthentication = true;
public bool RequireAuthentication
{
get { return requireAuthentication; }
set { requireAuthentication = value; }
}
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext ActionContext)
{
if (Authenticate(ActionContext) || !RequireAuthentication)
{
return;
}
else
{
HandleUnauthorizedRequest(ActionContext);
}
}
protected override void HandleUnauthorizedRequest(HttpActionContext ActionContext)
{
var challengeMessage = new System.Net.Http.HttpResponseMessage(HttpStatusCode.Unauthorized);
challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
throw new HttpResponseException(challengeMessage);
}
private bool Authenticate(System.Web.Http.Controllers.HttpActionContext ActionContext)
{
if (RequireSsl && !HttpContext.Current.Request.IsSecureConnection && !HttpContext.Current.Request.IsLocal)
{
//TODO: Return false to require SSL in production - disabled for testing before cert is purchased
//return false;
}
if (!HttpContext.Current.Request.Headers.AllKeys.Contains("Authorization")) return false;
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
IPrincipal principal;
if (TryGetPrincipal(authHeader, out principal))
{
HttpContext.Current.User = principal;
return true;
}
return false;
}
private bool TryGetPrincipal(string AuthHeader, out IPrincipal Principal)
{
var creds = ParseAuthHeader(AuthHeader);
if (creds != null)
{
if (TryGetPrincipal(creds[0], creds[1], creds[2], out Principal)) return true;
}
Principal = null;
return false;
}
private string[] ParseAuthHeader(string authHeader)
{
if (authHeader == null || authHeader.Length == 0 || !authHeader.StartsWith("Basic")) return null;
string base64Credentials = authHeader.Substring(6);
string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(base64Credentials)).Split(new char[] { ':' });
if (credentials.Length != 3 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1]) || string.IsNullOrEmpty(credentials[2])) return null;
return credentials;
}
private bool TryGetPrincipal(string Username, string ApiKey, string RequestHash, out IPrincipal Principal)
{
Username = Username.Trim();
ApiKey = ApiKey.Trim();
RequestHash = RequestHash.Trim();
//is valid username?
IUserRepository userRepository = new UserRepository();
UserModel user = null;
try
{
user = userRepository.GetUserByUsername(Username);
}
catch (UserNotFoundException)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
//is valid apikey?
IApiRepository apiRepository = new ApiRepository();
ApiModel api = null;
try
{
api = apiRepository.GetApi(new Guid(ApiKey));
}
catch (ApiNotFoundException)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
if (user != null)
{
//check if in allowed role
bool isAllowedRole = false;
string[] userRoles = System.Web.Security.Roles.GetRolesForUser(user.Username);
string[] allowedRoles = Roles.Split(','); //Roles is the inherited AuthorizeAttribute.Roles member
foreach(string userRole in userRoles)
{
foreach (string allowedRole in allowedRoles)
{
if (userRole == allowedRole)
{
isAllowedRole = true;
}
}
}
if (!isAllowedRole)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
Principal = new GenericPrincipal(new GenericIdentity(user.Username), userRoles);
Thread.CurrentPrincipal = Principal;
return true;
}
else
{
Principal = null;
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
}
}
自定义authorize属性控制以下控制器:
public class RequestKeyAuthorizeTestController : ApiController
{
[RequestKeyAuthorizeAttribute(Roles="Admin,Bob,Administrator,Clue")]
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.OK, "RequestKeyAuthorizeTestController");
}
}
在自定义RoleProvider中,我有这个方法:
public override string[] GetRolesForUser(string Username)
{
IRoleRepository roleRepository = new RoleRepository();
RoleModel[] roleModels = roleRepository.GetRolesForUser(Username);
List<string> roles = new List<string>();
foreach (RoleModel roleModel in roleModels)
{
roles.Add(roleModel.Name);
}
return roles.ToArray<string>();
}
答案 1 :(得分:0)
因此问题不在于您如何实现角色提供程序,而在于如何配置应用程序以使用它。但是,我在配置中找不到任何问题。请确保这确实是您配置应用程序的方式。这篇文章可能有所帮助:http://brianlegg.com/post/2011/05/09/Implementing-your-own-RoleProvider-and-MembershipProvider-in-MVC-3.aspx。如果您使用默认的MVC模板来创建项目,请检查AccountController。根据该帖子,您可能需要进行一些修改才能使自定义成员资格提供程序工作。但这不会影响角色提供者。
最诚挚的问候,
徐明。答案 2 :(得分:0)
我不喜欢自定义授权属性,因为我必须提醒用户使用它。我选择实施我自己的IIdentity
/ IPrincipal
课程,并在授权时将其连接起来。
调用默认UserIdentity
的自定义RoleProvider
:
public class UserIdentity : IIdentity, IPrincipal
{
private readonly IPrincipal _original;
public UserIdentity(IPrincipal original){
_original = original;
}
public string UserId
{
get
{
return _original.Identity.Name;
}
}
public string AuthenticationType
{
get
{
return _original.Identity.AuthenticationType;
}
}
public bool IsAuthenticated
{
get
{
return _original.Identity.IsAuthenticated;
}
}
public string Name
{
get
{
return _original.Identity.Name;
}
}
public IIdentity Identity
{
get
{
return this;
}
}
public bool IsInRole(string role){
return Roles.IsUserInRole(role);
}
}
并将其添加到global.asax.cs:
void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
if(false == HttpContext.Current.User is UserIdentity){
HttpContext.Current.User = new UserIdentity(HttpContext.Current.User);
}
}
答案 3 :(得分:0)
stimms
在评论中写道:&#34;我看到的是IPrincipal似乎没有正确的RoleProvider设置&#34;让我看看继承自Attribute
和IAuthenticationFilter
的自定义身份验证属性的实现。
using System.Web.Security;
....
protected override async Task<IPrincipal> AuthenticateAsync(string userName, string password, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(userName) || string.IsNullOrWhiteSpace(password))
{
// No user with userName/password exists.
return null;
}
var membershipProvider = Membership.Providers["CustomMembershipProvider"];
if (membershipProvider != null && membershipProvider.ValidateUser(userName, password))
{
ClaimsIdentity identity = new GenericIdentity(userName, "Basic");
return new RolePrincipal("CustomRoleProvider", identity);
}
return null;
}
关键是返回RolePrincipal
,它指向您的自定义角色提供程序。
最初我返回new ClaimsPrincipal(identity)
,这给了我OP中描述的问题。