最近开始接触asp.net,开发一个在线考试系统,其中涉及到用户管理和权限控制,要求如下:
1. 用户需要经过验证方能进入系统;
2. 不同级别的用户登录后看到的内容不同;
3. 管理员可进行管理操作,如角色、用户、权限的增删改查;
4. 防止非系统用户直接在地址栏输入某一页面地址进入系统,从而绕过登录页面;
5. 防止低权限用户登录系统后在地址栏输入高权限用户才能访问的某一页面地址,从而实现越权访问;
6. 同一用户多地登录,则后登录的挤掉先登录的,类似 QQ对于第2个问题可以采用重定向的方法来解决
在网站创建Admin和Tester两个文件夹,分别存放管理员和考试人员使用的文件,然后在登录界面进行重定向。
//返回重定向URL
string returnUrl = FormsAuthentication.GetRedirectUrl(loginUser.UserID, false);
if (returnUrl.Equals(FormsAuthentication.DefaultUrl))
{
//根据角色,确定跳转页面
if (RoleCheck.IsAdmin())
returnUrl = "~/Admin/Default.aspx";
else
returnUrl = "~/Tester/Default.aspx";
}
//重新定向到指定的URL页面
Response.Redirect(returnUrl);
对于第4个问题可以采用Session来实现,在本项目中新建一个SessionClass类,在里面实现两个获取和设置UserID的方法
public static Entity.User GetLoginUser()
{
if (null == HttpContext.Current.Session["loginUser"])
return null;
return (Entity.User)Tool.SerializeHelper.DeserializeObject(HttpContext.Current.Session["loginUser"].ToString());
}
public static void SetLoinUser(Entity.User loginUser)
{
if(loginUser!=null)
HttpContext.Current.Session["loginUser"] =Tool.SerializeHelper.SerializeObject(loginUser);
}
对于第5个问题,本项目新建一个RoleCheck类,对登录人员进行验证
public static class RoleCheck
{
private const string ADMIN_ROLE = "管理员";
public static bool IsAdmin()
{
Entity.User user=SessionClass.GetLoginUser();
if (user != null)
{
foreach (string right in user.RightList)
{
if(ADMIN_ROLE.Equals(right.Trim()))
return true;
}
}
return false;
}
/// <summary>
/// 拒绝非管理员用户的登录,拒绝后将跳转到登陆页面
/// </summary>
public static void DenyLogin()
{
if (!IsAdmin())
HttpContext.Current.Response.Redirect(string.Format("~/LoginSystem.aspx?errorMsg={0}","您的权限不是管理员,无法访问该页面!(也有可能是登录超时,请重新登录)"));
}
}
对于最后一个问题,通过SessionID和用户名来保证同一用户不能同时登录。步骤如下:
1.采用Application全局变量来存储SessionID和用户名,每次登录时都保存,并且将Application保存到Hashtable中,当用户登录成功后,首先判断该用户是否已经存储在Application中,如果存在(说明已经登录),则将该用户对应的值设置为XX。
protected void loginbtn_Click(object sender, EventArgs e)
{
//登录成功。。。。。。。
Hashtable hOnline = (Hashtable)Application["Online"];// 读取全局变量
if (hOnline != null)
{
IDictionaryEnumerator idE = hOnline.GetEnumerator();
string strKey = "";
while (idE.MoveNext())
{
if (idE.Value != null && idE.Value.ToString().Equals(UserID))//如果当前用户已经登录,
{
//already login
strKey = idE.Key.ToString();
hOnline[strKey] = "XX";//将当前用 户已经在全局变量中的值设置为XX
break;
}
}
}
else
{
hOnline = new Hashtable();
}
hOnline[Session.SessionID] = UserID;//初始化当前用户的
Application.Lock();
Application["Online"] = hOnline;
Application.UnLock();
Response.Redirect("main.aspx");
}
2,写一个BasePage加一个Init方法如下,系统的所有页面(除了登录和退出页面)均继承自该BasePage类
protected override void OnInit(EventArgs e)
{
Hashtable hOnline = (Hashtable)Application["Online"];//获取已经存储的application值
if(hOnline != null)
{
IDictionaryEnumerator idE = hOnline.GetEnumerator();
while(idE.MoveNext())
{
if(idE.Key != null && idE.Key.ToString().Equals(Session.SessionID))
{
if (idE.Value != null && "XX".Equals(idE.Value.ToString()))//说明在别处登录
{
hOnline.Remove(Session.SessionID);
Application.Lock();
Application["Online"] = hOnline;
Application.UnLock();
Response.Write("script type="text/javascript">
alert('你的帐号已在别处登陆,你被强迫下线!');top.location.href='Default.aspx';window.close();</script>"); Response.Redirect("Default.aspx");
Response.End();
}
}
}
}
3.在程序退出后从Application中清除当前SessionID
void Session_End(object sender, EventArgs e)
{
// 在会话结束时运行的代码。
// 注意: 只有在 Web.config 文件中的 sessionstate 模式设置为
// InProc 时,才会引发 Session_End 事件。如果会话模式设置为 StateServer
// 或 SQLServer,则不会引发该事件。
Hashtable hOnline = (Hashtable)Application["Online"];
if (hOnline[Session.SessionID] != null)
{
hOnline.Remove(Session.SessionID);//清除当前SessionID
Application.Lock();
Application["Online"] = hOnline;
Application.UnLock();
}
}
4.如果程序非正常退出,SessionID没有及时的清除,那么也不会影响帐号的正常登录,而SessionID也会随着Session的过期而自动清除,服务器也不会有压力。
5,如果感觉存储在Application中感觉不好,也可以将SessionID存入数据库中,判断方法和上面一样。