ASP.NET Portal Starter Kit 是采用的“基于窗体的身份验证”的身份验证模式。 Forms 身份验证通常指这样一个系统,在该系统中使用 HTTP 客户端重定向将未经身份验证的请求重定向到 HTML 窗体。如果应用程序需要在登录时通过 HTML 窗体收集自己的用户凭据,那么选择 Forms 身份验证就很好。用户提供凭据并提交该窗体。如果应用程序对请求进行身份验证,系统会发出一个 Cookie ,在其中包含用于重新获取标识的凭据或密钥。随后发出在请求头中具有该 Cookie 的请求。 ASP.NET 事件处理程序使用应用程序指定的任何验证方法对这些请求进行身份验证和授权。
数据库设计:
在 ASP.NET Portal Starter Kit 中存储用户角色相关的表有三个:用户信息表( Portal_Users ),角色信息表( Portal_Roles ),用户角色关系表( Portal_UserRoles )。通过用户角色关系表将用户信息和角色信息管理起来,可实现一个用户可有多种角色,一个角色也可以同时是多个用户。三表之间的关系如下:
程序实现:
在 ASP.NET Portal Starter Kit 中用户登录成功后以 email 为用户标识名称建立用户标识时同时触发 Global.asax.cs 中的 Application_AuthenticateRequest 事件,将登录用户的角色信息读入 Context.User 中。在DesktopDefault.aspx根据portalSettings.ActiveTab.AuthorizedRoles(当前活动标签的可访问属性)判断是否可访问该标签的内容。如果可访问则呈现该标签下的用户模块,不能访问就重定向到访问错误页(AccessDenied.aspx)。在管理用户是否可编辑用户模块信息时,是判断用户角色是否在模块指定的可编辑角色(模块的IsEditable属性)中。
关键代码:
1、 根据用户的Email获取用户的角色(以String[]的形式返回,一项表示一个角色,一个用户可有多个角色)(Security.cs中)
public String[] GetRoles(String email) { // 访问数据库的几步曲 SqlConnection myConnection = new SqlConnection(ConfigurationSettings.AppSettings[ " connectionString " ]); SqlCommand myCommand = new SqlCommand( " Portal_GetRolesByUser " , myConnection); myCommand.CommandType = CommandType.StoredProcedure; SqlParameter parameterEmail = new SqlParameter( " @Email " , SqlDbType.NVarChar, 100 ); parameterEmail.Value = email; myCommand.Parameters.Add(parameterEmail); // 打开链接用SqlDataReader执行查询 SqlDataReader dr; myConnection.Open(); dr = myCommand.ExecuteReader(CommandBehavior.CloseConnection); // 读取用户的角色信息 ArrayList userRoles = new ArrayList(); while (dr.Read()) { userRoles.Add(dr[ " RoleName " ]); } dr.Close(); return (String[]) userRoles.ToArray( typeof (String)); }
2、 检查当前角色是否在指定的角色中(Security.cs中)
public static bool IsInRoles(String roles) { HttpContext context = HttpContext.Current; foreach (String role in roles.Split( new char [] { ' ; ' } )) { // 指定角色中有All Users的也表示通过 if (role != "" && role != null && ((role == " All Users " ) || (context.User.IsInRole(role)))) { return true ; } } return false ; }
3 、登录验证代码(SignIn.ascx.cs中)
private void SigninBtn_Click( object sender, System.Web.UI.ImageClickEventArgs e) { // 通过UsersDB类尝试并验证用户是否合法 UsersDB accountSystem = new UsersDB(); String userId = accountSystem.Login(email.Text, PortalSecurity.Encrypt(password.Text)); if ((userId != null ) && (userId != "" )) { // 为给定的 userName 和 createPersistentCookie 创建身份验证票,并将其附加到 Cookie 的传出响应的集合。它不执行重定向。 // 以email为用户标识名称建立用户标识时同时触发Global.asax.cs中的Application_AuthenticateRequest事件 FormsAuthentication.SetAuthCookie(email.Text, RememberCheckbox.Checked); // 从定向到起始页 Response.Redirect(Request.ApplicationPath); } else { Message.Text = " < " + " br " + " >登录失败! " + " < " + " br " + " > " ; } }
4 、 Global.asax.cs 中的 Application_AuthenticateRequest 事件
protected void Application_AuthenticateRequest(Object sender, EventArgs e) { if (Request.IsAuthenticated == true ) { String[] roles; // 将用户角色信息存入到cookie if ((Request.Cookies[ " portalroles " ] == null ) || (Request.Cookies[ " portalroles " ].Value == "" )) { // 当Cookies中没有时,从数据库中读取 UsersDB user = new UsersDB(); roles = user.GetRoles(User.Identity.Name); // 以字符串的形式存储用户角色信息用";"分隔,一个用户的多个角色 String roleStr = "" ; foreach (String role in roles) { roleStr += role; roleStr += " ; " ; } // 创建身份角色验证的凭据 FormsAuthenticationTicket ticket = new FormsAuthenticationTicket( 1 , // version 版本 Context.User.Identity.Name, // user name cookie 名 DateTime.Now, // issue time 发布日期 DateTime.Now.AddHours( 1 ), // expires every hour 过期日期 false , // don't persist cookie 持久性(false) roleStr // roles 用户定义的数据初始化(";"分隔的角色字符串) ); // 加密凭证 String cookieStr = FormsAuthentication.Encrypt(ticket); // 将存有用户角色信息的字符串存入cookie Response.Cookies[ " portalroles " ].Value = cookieStr; Response.Cookies[ " portalroles " ].Path = " / " ; Response.Cookies[ " portalroles " ].Expires = DateTime.Now.AddMinutes( 1 ); } else { // 当Cookies中有时,从Cookies中读取 FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(Context.Request.Cookies[ " portalroles " ].Value); ArrayList userRoles = new ArrayList(); foreach (String role in ticket.UserData.Split( new char [] { ' ; ' } )) { userRoles.Add(role); } roles = (String[]) userRoles.ToArray( typeof (String)); } // 从 GenericIdentity 和角色名称数组(GenericIdentity 表示的用户属于该数组)初始化 GenericPrincipal 类的新实例。 Context.User = new GenericPrincipal(Context.User.Identity, roles); } }
5 、DesktopDefault.aspx中的判断是否可访问该页的代码
// 当前用户的角色不在当前活动标签的可访问角色中时,重定向到访问错误页 if (PortalSecurity.IsInRoles(portalSettings.ActiveTab.AuthorizedRoles) == false ) { Response.Redirect( " ~/Admin/AccessDenied.aspx " ); }
6 、用户是否可编辑用户模块的代码
public static bool HasEditPermissions( int moduleId) { string accessRoles; string editRoles; // 获取站点的设置信息 SiteConfiguration siteSettings = (SiteConfiguration) HttpContext.Current.Items[ " SiteSettings " ]; // 在设置信息中找到指定模块的行(XML中的用户模块表Module) SiteConfiguration.ModuleRow moduleRow = siteSettings.Module.FindByModuleId(moduleId); // 可编辑指定模块的角色信息 editRoles = moduleRow.EditRoles; // 可访问模块所属标签的角色信息 accessRoles = moduleRow.TabRow.AccessRoles; // 既有模块的编辑权,又有模块所属标签的访问权的才可修改指定模块 if (PortalSecurity.IsInRoles(accessRoles) == false || PortalSecurity.IsInRoles(editRoles) == false ) return false ; else return true ; }