forms认证与session认证的学习总结
以往用forms认证和session做登陆验证,这几天仔细看了一下这方面的一些知识,发现以前很多概念都是很模糊和错误的。所以想总结一下。
在用asp.net做网站登录验证的时候,让网站或者系统后台必须登陆才能访问时,很多时候会用到forms认证,也有些人同时用session加一层验证。
Forms认证
forms认证,用的是cookie来进行验证,cookie是保存在客户端的,其安全性来说我也不是很清楚,都说cookie保存在客户端很容易被截取被改动,但我认为一般的人很难截取
cookie,至少我搜了一晚上也没找到怎么截取后的cookie来实现模仿登陆,可能我很菜不知道,求高人指点。所以我认为,在一般的网站中只用forms认证相对来说已经够用了。forms认证的配置也很简单,用mvc上的一个例子,只需要在Web.config中的
内加上配置节,如: 1
2
3
4
5<system.web>
<authentication mode="Forms">
<forms loginUrl="~/Home/Login" timeout="60" />
</authentication>
</system.web>并且在登陆验证账号密码正确后,调用这个方法来保存cookie,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private void SetCookie(string userName,string userRole)
{
//加cookie
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,//版本号
userName,//用户名
DateTime.Now,//生成票据的时间
DateTime.Now.AddMinutes(30),//cookie过期时间
false,//cookie是持久的为true,否则为false
userRole//用于辨认用户角色
);
string eTicket = FormsAuthentication.Encrypt(authTicket);//加密cookie
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, eTicket);
authCookie.HttpOnly=true ;//防止javascript访问,可提高安全性能
//authCookie.Secure = true;这个为true时候,只有https才能访问
Response.Cookies.Add(authCookie);
//当检测cookie快过期,会自动扩展,不必监听
///1.userName可以用来存储用户名(比如学生学号),这个字段可以当做全局的来调用,如插入数据的时候填写记录用户名,在后台可以直接用User.Identity.Name直接调用userName
//2.userRole可用来标识身份,如一个选课系统中,管理员是admin,教师是teacher,学生是student
//想要用调用userRole时候可以用下面的方法
//var cookie = Request.Cookies[FormsAuthentication.FormsCookieName];
//var ticket = FormsAuthentication.Decrypt(cookie.Value);
//string role = ticket.UserData;
//这也可以调用userName
//string name=ticket.Name;
}这样做后,只需要在控制器或者方法上添加 [Authorize]就行,当未登录用户直接访问当前方法的时候,就会跳转到web.config配置的路径了,达到限制访问的效果,如下面:
1
2
3
4
5[Authorize]
public ActionResult Index()
{
return View();
}但这种做法只能识别一种身份的,就是说只能识别登陆与未登录用户,并不会识别当前的访问者是什么身份,如教师和学生,当我们想要做到教师可以访问,学生不可访问这种方法,还得在Global.asax添加这一段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25//forms认证需要,在控制器上加上[Authorize(Roles = "admin")]可控制权限
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null || authCookie.Value == "")
{
//Construst the GeneralPrincipal and FormsIdentity objects
return;
}
FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch
{
return;
}
//get the role
string[] roles = authTicket.UserData.Split(new char[] { ';' });
if (Context.User != null)
{
Context.User = new System.Security.Principal.GenericPrincipal(Context.User.Identity, roles);
}
}然后通过在控制器或方法上面添加[Authorize(Roles = “xxx”)]就可以做到这个方法限制只能是xxx这种用户才可以访问,如
1
2
3
4
5
6[Authorize(Roles = "admin")]//admin是我在登陆设置的cookie中的UserRole
public ActionResult Index()
{
return View();
}
//这样就可以做到只有admin标识的用户才可访问,其他如教师、学生访问这个方法就会跳转到登陆页。
当然这只是MVC上的做法,WebForm形式的校验方法其实更简单,这里就不多说,google一下一大把。
Session认证
session验证,一般很多人都会用这个来验证登陆,这可以说是最简单的了。用session验证登陆其实比forms认证是更安全一些的,毕竟session是保存在服务器端的,被篡改的可能更难。但session是保存在服务器的内存中的(有些人说是保存在文件中的,我也没搞清楚到底是在哪,但我现在认为默认是保存在内存中的),所以说session是会消耗服务器资源的,当网站访问量很大的时候,session的数量就很可观了,当服务器内存不足或者其他原因就崩溃了。
对于webform,只要在pageload里面验证一下if(Session[“xxx”]==null) Resopnse.Redirect(“../login.aspx”);也就是说当session为空的时候就跳转到登陆页面。如:1
2
3
4
5
6
7protected void Page_Load(object sender, EventArgs e)
{
if(Session["user"]==null)
{
Response.Redirect("~/login.aspx");
}
}但这样做有点麻烦,就是需要每个页面都要在PageLoad里面判断session是否为空。这时候最好就写一个继承的类,让需要验证登陆的页面都继承一个BasePage类,在BasePage页面override OnInit事件(BasePage页面要继承System.Web.UI.Page),这样就可以实现当请求子页面时候都验证一次session而且不用每个页面都复制粘贴代码了。如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19protected override void OnInit(EventArgs e)
{
if (Session["uInfo"] == null)
{
//1.判断用户是否勾选记住三天免登陆
if (Request.Cookies[“uid”] != null)
{
BlogUserBLL bll = new BlogUserBLL();
//2.通过cookie里传入的数据得到数据实体
BlogUser umodel = bll.GetModel(int.Parse(Request.Cookies["uid"].Value));
//3.将实体存入session中
Session["uInfo"] = umodel;
return;
}
//4.跳转到登陆页面
Response.Redirect("/Login.aspx");
}
base.OnInit(e);
}而对于MVC下的,就写一个BaseController的类,在BaseController里面写验证session,需要验证登陆的页面只要继承BaseController类就可以了。如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30public class BaseController :Controller
{
//在当前的控制器里面所有的方法执行之前。都先执行此代码
public bool IsCheckUserLogin = true;
public UserInfo LoginUser { get; set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
var items = filterContext.RouteData.Values;
if (IsCheckUserLogin)
{
if (filterContext.HttpContext.Session["loginUser"] == null)
{
filterContext.HttpContext.Response.Redirect("/UserLogin/Index");
}
else
{
LoginUser = filterContext.HttpContext.Session["loginUser"] as UserInfo;
}
}
}
}
Forms和Session双认证
在刚学到forms认证时候,也没有真正弄清楚forms认证和session认证是怎么回事,当时觉得反正大家都这样用,我也这样做,肯定就不会错了。所以,很多时候我们就是session和Forms认证一起用,在pageload里面验证session是否为空,又用forms认证,其实这样做也挺好的,不过当时没有意识到用BasePage类,在每个页面都是复制粘贴代码,导致很乱很难维护,一直也没理清是怎么回事。
在一般的项目中,同时用session和forms我认为是最安全的,一般也建议这样做。
总结
在我一般的建站中,用到session的时候一般是登陆的时候记录一下用户名,以便在用户操作时候记录这个用户的用户名。
校内的服务器性能比较差,要把内存和带宽的利用做得很好,才能承受更大的访问量。用cookie会在每次访问页面时发送到服务器,影响到服务器带宽,而session则会消耗服务器内存。
所以,最后我认为,为了减少服务器负担,在服务器不给力和要满足一定的安全前提下,尽量少用session存储数据量较大的数据。我用了forms认证,就没必要再用session了。当需要用到全局的用户名的时候,直接用cookie记录用户的身份(管理员、教师、学生)和当前访问人的用户名,调用cookie就好了。
以上