问题描述
我在用ASP.net MVC4自定义角色提供程序的问题。我实现了一个重量很轻的RoleProvider这似乎做工精细的权利,直到我换
[授权]
公共类BlahController:....
}
到
[授权(角色=管理员)]
公共类BlahController:....
}
只要我做出的改变用户不再验证,我得到401错误。这是奇怪,因为我基本上RoleProvider为返回的isUserInRole和含有GetUserRoles将管理员列表真。我曾在地方断点在我的自定义RoleProvider每一个方法,发现他们无一被调用。
接下来我实现了从AuthorizeAttribute继承了我自己的授权属性。在此我把破发点,所以我可以看到发生了什么事情。原来,User.IsInRole(),它是由基础属性被称为返回false。
我相信,角色提供的设置是否正确。我有这个在我的配置文件
< roleManager启用=真正的defaultProvider =SimplicityRoleProvider>
<供应商>
<清/>
<添加名称=SimplicityRoleProviderTYPE =Simplicity.Authentication.SimplicityRoleProvider的applicationName =简单/>
< /供应商>
< / roleManager>
和检查哪个角色提供程序是当前使用的方法描述如下:参考电流RoleProvider例如产生正确的结果。然而User.IsInRole坚持返回false。
我使用Azure的访问控制服务,但我看不出这将是一个自定义角色提供程序不兼容。
我能做些什么来纠正用户的IPrincipal这样IsInRole返回从我的自定义RoleProvider的价值?
RoleProvider来源:
公共类SimplicityRoleProvider:RoleProvider
{
私人ILog的日志{搞定;组; }
公共SimplicityRoleProvider()
{
登录= LogManager.GetLogger(FF);
} 公共覆盖无效AddUsersToRoles(字符串[]用户名,字符串[]角色名)
{
log.Warn(用户名);
log.Warn(角色名);
} 公众覆盖字符串的应用程序名称
{
得到
{
回归简单;
}
组
{ }
} 公共覆盖无效CREATEROLE(字符串角色名)
{ } 公众覆盖布尔DeleteRole(字符串角色名,布尔throwOnPopulatedRole)
{
返回true;
} 公共重写字符串[] FindUsersInRole(字符串角色名,字符串的usernameToMatch)
{
log.Warn(角色名);
log.Warn(的usernameToMatch);
返回新的字符串[0];
} 公共重写字符串[] GetAllRoles()
{
log.Warn(所有角色);
返回新的字符串[0];
} 公共重写字符串[] GetRolesForUser(用户名字符串)
{
log.Warn(用户名);
返回新的String [] {管理员};
} 公共重写字符串[] GetUsersInRole(字符串角色名)
{
log.Warn(角色名);
返回新的字符串[0];
} 公众覆盖布尔的isUserInRole(用户名字符串,字符串角色名)
{
log.Warn(用户名);
log.Warn(角色名);
返回true;
} 公共覆盖无效RemoveUsersFromRoles(字符串[]用户名,字符串[]角色名)
{ } 公众覆盖布尔RoleExists(字符串角色名)
{
log.Warn(角色名);
返回true;
}
}
似乎System.Web.Security.Roles.GetRolesForUser(用户名)不会被自动挂接,当你有一个自定义AuthorizeAttribute和一个自定义RoleProvider。
那么,在您的自定义AuthorizeAttribute你需要从数据源中检索角色列表,然后比较他们对作为参数传递给AuthorizeAttribute的角色。
我看到一对夫妇的博客文章评论暗示手动比较的角色是没有必要的,但是当我们覆盖AuthorizeAttribute看来,我们是燮pressing这种行为,需要向它提供自己。
无论如何,我会通过我什么工作走。希望这会有些帮助的。
我欢迎是否有更好的方式来做到这一点的意见。
请注意,在我的情况下,AuthorizeAttribute被应用于ApiController虽然我不知道这是一个相关的资料片。
公共类RequestHashAuthorizeAttribute:AuthorizeAttribute
{
布尔requireSsl = TRUE; 公共BOOL RequireSsl
{
{返回requireSsl; }
集合{requireSsl =价值; }
} 布尔requireAuthentication = TRUE; 公共BOOL RequireAuthentication
{
{返回requireAuthentication; }
集合{requireAuthentication =价值; }
} 公共覆盖无效OnAuthorization(System.Web.Http.Controllers.HttpActionContext ActionContext中)
{
如果(认证(ActionContext中)||!RequireAuthentication)
{
返回;
}
其他
{
HandleUnauthorizedRequest(ActionContext中);
}
} 保护覆盖无效HandleUnauthorizedRequest(HttpActionContext ActionContext中)
{
VAR challengeMessage =新System.Net.Http.Htt presponseMessage(的HTTPStatus code.Unauthorized);
challengeMessage.Headers.Add(WWW身份验证,基础);
抛出新的Htt presponseException(challengeMessage);
} 私人布尔身份验证(System.Web.Http.Controllers.HttpActionContext ActionContext中)
{
如果(RequireSsl&安培;&安培;!HttpContext.Current.Request.IsSecureConnection和放大器;&安培;!HttpContext.Current.Request.IsLocal)
{
// TODO:返回false要求生产SSL - 购买证书之前进行测试禁用
//返回false;
} 如果(HttpContext.Current.Request.Headers.AllKeys.Contains(授权)!)返回false; 字符串authHeader = HttpContext.Current.Request.Headers [授权]; 主要的IPrincipal;
如果(TryGetPrincipal(authHeader,出校长))
{
HttpContext.Current.User =本金;
返回true;
}
返回false;
} 私人布尔TryGetPrincipal(字符串AuthHeader,出来的IPrincipal校长)
{
VAR creds = ParseAuthHeader(AuthHeader);
如果(creds!= NULL)
{
如果(TryGetPrincipal(creds [0],creds [1],creds [2],主要出))返回true;
} 校长= NULL;
返回false;
} 私人的String [] ParseAuthHeader(字符串authHeader)
{
如果(authHeader == NULL || authHeader.Length == 0 || authHeader.StartsWith(基本)!)返回NULL; 串base64Credentials = authHeader.Substring(6);
字符串[] =凭证Encoding.ASCII.GetString(Convert.FromBase64String(base64Credentials))斯普利特(新的char [] {':'}); 如果(credentials.Length = 3 || string.IsNullOrEmpty(凭证[0])|| string.IsNullOrEmpty(凭证[1])|| string.IsNullOrEmpty(凭证[2])!)返回零; 返回证书;
} 私人布尔TryGetPrincipal(用户名字符串,字符串ApiKey,串RequestHash,出来的IPrincipal校长)
{
用户名= Username.Trim();
ApiKey = ApiKey.Trim();
RequestHash = RequestHash.Trim(); //是有效的用户名?
IUserRepository userRepository =新UserRepository();
用户的usermodel = NULL;
尝试
{
用户= userRepository.GetUserByUsername(用户名);
}
赶上(UserNotFoundException)
{
抛出新的Htt presponseException(新的Htt presponseMessage(的HTTPStatus code.Unauthorized));
} //有效apikey?
IApiRepository apiRepository =新ApiRepository();
ApiModel API = NULL;
尝试
{
API = apiRepository.GetApi(新的GUID(ApiKey));
}
赶上(ApiNotFoundException)
{
抛出新的Htt presponseException(新的Htt presponseMessage(的HTTPStatus code.Unauthorized));
} 如果(用户!= NULL)
{
//检查是否允许中的作用
布尔isAllowedRole = FALSE;
字符串[] =的UserRole System.Web.Security.Roles.GetRolesForUser(user.Username);
串[] allowedRoles = Roles.Split(,); //角色是继承AuthorizeAttribute.Roles成员
的foreach(字符串的UserRole中的UserRole)
{
的foreach(字符串allowedRole在allowedRoles)
{
如果(UserRole的== allowedRole)
{
isAllowedRole =真;
}
}
} 如果(!isAllowedRole)
{
抛出新的Htt presponseException(新的Htt presponseMessage(的HTTPStatus code.Unauthorized));
} 校长=新的GenericPrincipal(新GenericIdentity(user.Username)的UserRole);
= Thread.CurrentPrincipal中校长; 返回true;
}
其他
{
校长= NULL;
抛出新的Htt presponseException(新的Htt presponseMessage(的HTTPStatus code.Unauthorized));
}
}
}
自定义授权属性执政以下控制器:
公共类RequestKeyAuthorizeTestController:ApiController
{
[RequestKeyAuthorizeAttribute(角色=管理员,鲍勃,管理员,线索)]
公众的Htt presponseMessage的get()
{
返回Request.CreateResponse(的HTTPStatus code.OK,RequestKeyAuthorizeTestController);
}
}
在自定义RoleProvider,我有这样的方法:
公众覆盖字符串[] GetRolesForUser(字符串用户名)
{
IRoleRepository roleRepository =新RoleRepository();
RoleModel [] = roleModels roleRepository.GetRolesForUser(用户名); 清单<串GT;角色=新的List<串GT;(); 的foreach(在roleModels RoleModel roleModel)
{
roles.Add(roleModel.Name);
} 返回roles.ToArray<串GT;();
}
I'm having an issue with a custom role provider in ASP.net MVC4. I implemented a very light weight RoleProvider which seems to work fine right up until I change
[Authorize]
public class BlahController:....
}
to
[Authorize(Roles="Administrator")]
public class BlahController:....
}
as soon as I make that change users are no longer authenticated and I get 401 errors. This is odd because my RoleProvider basically returns true for IsUSerInRole and a list containing "Administrator" for GetUserRoles. I had breakpoints in place on every method in my custom RoleProvider and found that none of them were being called.
Next I implemented my own authorize attribute which inherited from AuthorizeAttribute. In this I put in break points so I could see what was going on. It turned out that User.IsInRole(), which is called by the underlying attribute was returning false.
I am confident that the role provider is properly set up. I have this in my config file
<roleManager enabled="true" defaultProvider="SimplicityRoleProvider">
<providers>
<clear />
<add name="SimplicityRoleProvider" type="Simplicity.Authentication.SimplicityRoleProvider" applicationName="Simplicity" />
</providers>
</roleManager>
and checking which role provider is the current one using the method described here: Reference current RoleProvider instance? yields the correct result. However User.IsInRole persists in returning false.
I am using Azure Access Control Services but I don't see how that would be incompatible with a custom role provider.
What can I do to correct the IPrincipal User such that IsInRole returns the value from my custom RoleProvider?
RoleProvider source:
public class SimplicityRoleProvider : RoleProvider { private ILog log { get; set; }
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;
}
}
It seems that System.Web.Security.Roles.GetRolesForUser(Username) does not get automatically hooked up when you have a custom AuthorizeAttribute and a custom RoleProvider.
So, in your custom AuthorizeAttribute you need to retrieve the list of roles from your data source and then compare them against the roles passed in as parameters to the AuthorizeAttribute.
I have seen in a couple blog posts comments that imply manually comparing roles is not necessary but when we override AuthorizeAttribute it seems that we are suppressing this behavior and need to provide it ourselves.
Anyway, I'll walk through what worked for me. Hopefully it will be of some assistance.
I welcome comments on whether there is a better way to accomplish this.
Note that in my case the AuthorizeAttribute is being applied to an ApiController although I'm not sure that is a relevant piece of information.
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));
}
}
}
The custom authorize attribute is governing the following controller:
public class RequestKeyAuthorizeTestController : ApiController
{
[RequestKeyAuthorizeAttribute(Roles="Admin,Bob,Administrator,Clue")]
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.OK, "RequestKeyAuthorizeTestController");
}
}
In the custom RoleProvider, I have this method:
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>();
}
这篇关于自RoleProvider失败时施加作用AuthorizeAttribute的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!