[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class RequestAuthorizeAttribute : AuthorizeAttribute { //验证 public override void OnAuthorization(AuthorizationContext context) { EnsureHelper.EnsureNotNull(context, "httpContent"); //是否允许匿名访问 if (context.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), false)) { return; } //登录验证 Principal principal = context.HttpContext.User as Principal; if (principal == null) { SetUnAuthorizedResult(context); HandleUnauthorizedRequest(context); return; } //权限验证 if (!principal.IsInRole(base.Roles) || !principal.IsInUser(base.Users)) { SetUnAuthorizedResult(context); HandleUnauthorizedRequest(context); return; } //验证配置文件 if(!ValidateAuthorizeConfig(principal, context)) { SetUnAuthorizedResult(context); HandleUnauthorizedRequest(context); return; } } //验证不通过时 private void SetUnAuthorizedResult(AuthorizationContext context) { HttpRequestBase request = context.HttpContext.Request; if (request.IsAjaxRequest()) { //处理ajax请求 string result = JsonConvert.SerializeObject(JsonModel.Error(403)); context.Result = new ContentResult() { Content = result }; } else { //跳转到登录页面 string loginUrl = FormsAuthentication.LoginUrl + "?ReturnUrl=" + preUrl; context.Result = new RedirectResult(loginUrl); } } //override protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if(filterContext.Result != null) { return; } base.HandleUnauthorizedRequest(filterContext); } }
注:这里的代码摘自个人项目中的,简写了部分代码,有些是辅助类,代码没有贴出,但应该不影响阅读。
1. 如果我们在HttpApplication的AuthenticateRequest事件中获得的IPrincipal为null,那么验证不通过。
2. 如果验证通过,程序会进行验证AuthorizeAttribute的Roles和User属性。
3. 如果验证通过,程序会验证配置文件中对应的Roles和Users属性。
验证配置文件的方法如下:
private bool ValidateAuthorizeConfig(Principal principal, AuthorizationContext context) { //action可能有重载,重载时应该标记ActionName区分 ActionNameAttribute actionNameAttr = context.ActionDescriptor .GetCustomAttributes(typeof(ActionNameAttribute), false) .OfType<ActionNameAttribute>().FirstOrDefault(); string actionName = actionNameAttr == null ? null : actionNameAttr.Name; AuthorizationConfig ac = ParseAuthorizeConfig(actionName, context.RouteData); if (ac != null) { if (!principal.IsInRole(ac.Roles)) { return false; } if (!principal.IsInUser(ac.Users)) { return false; } } return true; } private AuthorizationConfig ParseAuthorizeConfig(string actionName, RouteData routeData) { string areaName = routeData.DataTokens["area"] as string; string controllerName = null; object controller, action; if(string.IsNullOrEmpty(actionName)) { if(routeData.Values.TryGetValue("action", out action)) { actionName = action.ToString(); } } if (routeData.Values.TryGetValue("controller", out controller)) { controllerName = controller.ToString(); } if(!string.IsNullOrEmpty(controllerName) && !string.IsNullOrEmpty(actionName)) { return AuthorizationConfig.ParseAuthorizationConfig( areaName, controllerName, actionName); } return null; } }
可以看到,它会根据当前请求的area、controller和action名称,通过一个AuthorizationConfig类进行验证,该类的定义如下:
public class AuthorizationConfig { public string Roles { get; set; } public string Users { get; set; } private static XDocument _doc; //配置文件路径 private static string _path = "~/Identity/Authorization.xml"; //首次使用加载配置文件 static AuthorizationConfig() { string absPath = HttpContext.Current.Server.MapPath(_path); if (File.Exists(absPath)) { _doc = XDocument.Load(absPath); } } //解析配置文件,获得包含Roles和Users的信息 public static AuthorizationConfig ParseAuthorizationConfig(string areaName, string controllerName, string actionName) { EnsureHelper.EnsureNotNullOrEmpty(controllerName, "controllerName"); EnsureHelper.EnsureNotNullOrEmpty(actionName, "actionName"); if (_doc == null) { return null; } XElement rootElement = _doc.Element("root"); if (rootElement == null) { return null; } AuthorizationConfig info = new AuthorizationConfig(); XElement rolesElement = null; XElement usersElement = null; XElement areaElement = rootElement.Elements("area") .Where(e => CompareName(e, areaName)).FirstOrDefault(); XElement targetElement = areaElement ?? rootElement; XElement controllerElement = targetElement.Elements("controller") .Where(e => CompareName(e, controllerName)).FirstOrDefault(); //如果没有area节点和controller节点则返回null if (areaElement == null && controllerElement == null) { return null; } //此时获取标记的area if (controllerElement == null) { rootElement = areaElement.Element("roles"); usersElement = areaElement.Element("users"); } else { XElement actionElement = controllerElement.Elements("action") .Where(e => CompareName(e, actionName)).FirstOrDefault(); if (actionElement != null) { //此时获取标记action的 rolesElement = actionElement.Element("roles"); usersElement = actionElement.Element("users"); } else { //此时获取标记controller的 rolesElement = controllerElement.Element("roles"); usersElement = controllerElement.Element("users"); } } info.Roles = rolesElement == null ? null : rolesElement.Value; info.Users = usersElement == null ? null : usersElement.Value; return info; } private static bool CompareName(XElement e, string value) { XAttribute attribute = e.Attribute("name"); if (attribute == null || string.IsNullOrEmpty(attribute.Value)) { return false; } return attribute.Value.Equals(value, StringComparison.OrdinalIgnoreCase); } }
这里的代码比较长,但主要逻辑就是解析文章开头的配置信息。
简单总结一下程序实现的步骤:
1. 校对用户名和密码正确后,调用SetAuthenticationCookie将一些状态信息写入cookie。
2. 在HttpApplication的Authentication事件中,调用TryParsePrincipal获得状态信息。
3. 在需要验证的Action(或Controller)标记 RequestAuthorizeAttribute特性,并设置Roles和Users;Roles和Users也可以在配置文件中配置。
4. 在RequestAuthorizeAttribute的OnAuthorization方法中进行验证和权限逻辑处理。
四、总结