【转】权限管理学习 一、ASP.NET Forms身份认证
说明:本文示例使用的VS2017和MVC5。
系统无论大小、牛逼或屌丝,一般都离不开注册、登录。那么接下来我们就来分析下用户身份认证。
简单实现登录、注销
以前在学习.net的时候不知道什么Forms身份认证,直接用session实现登录,效果也蛮好嘛。而且用户信息存在服务端,安全。
前端代码:
@if (string.IsNullOrWhiteSpace(ViewBag.UserName)){ ???<form action="/home/login1"> ???????<input type="text" name="userName" /> ???????<input type="submit" value="登录" /> ???</form>}else{ ???<form action="/home/logout1"> ???????<div>当前用户已登录,登录名:@ViewBag.UserName</div> ???????<input type="submit" value="退出" /> ???</form>}
后台代码:
public ActionResult Index(){ ???ViewBag.UserName = Session["userName"]?.ToString(); ??????????????return View();} ??????public void Login1(string userName){ ???if (!string.IsNullOrWhiteSpace(userName)) ?//为了方便演示,就不做真的验证了 ????????????Session["userName"] = userName; ???else ???????Session["userName"] = null; ???Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原来页面}public void Logout1(){ ???Session["userName"] = null; ???Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原来页面}
是不是,简单明了。想要自己扩展或是定制什么功能都非常好用。不过我们需要维护session。比如系统重新发布,或者iis被自动重启。就会出现session丢失的情况。也就是用户会莫名其妙提升需要重新登录。体验非常不好。(这里先不讨论session服务和数据库的情况)。既然微软有一套成熟的权限管理我们为什么不用呢?
FORMS认证登录、注销
首先在web.config里开启Forms身份认证:
<system.web> ?<authentication mode="Forms"></authentication>
后台代码:
public void Login2(string userName){ ???if (!string.IsNullOrWhiteSpace(userName)) ?//为了方便演示,就不做真的验证了 ???????FormsAuthentication.SetAuthCookie(userName, true); //登录 ???Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原来页面}public void Logout2(){ ???FormsAuthentication.SignOut();//登出 ???Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原来页面}
前台代码:
@if (!Request.IsAuthenticated){ ???<form action="/home/login2"> ???????<input type="text" name="userName" /> ???????<input type="submit" value="登录" /> ???</form>}else{ ???<form action="/home/logout2"> ???????<div>当前用户已登录,登录名:@Context.User.Identity.Name</div> ???????<input type="submit" value="退出" /> ???</form>}
如此几句代码就实现了我们的登录和注销。和我们自己用session管理登录不同。Forms身份认证是直接把信息存cookie到浏览器的。通过SetAuthCookie这个方法名也可以看出来。不过Cookie信息经过了加密。
这里有必要说明session和cookie的关系。当我们利用session来维持用户状态的时候,其实也用到了cookie。
然而Forms身份认证仅仅只是把信息存了cookie,而没有在服务端维护一个对应的session。
不信你可以测试。可以用两种方式都登录,然后清除session就可以测出来了。(怎么清session?重启iis,或者修改下后台代码在重新编译访问)
【说明】用户认证为什么要存cookie?因为HTTP是一个无状态的协议。对于服务器来说,每次请求都是一样的。所以,只能通过每次请求带的cookie来识别用户了。(暂时不考虑其他方式)
自定义的身份认证标识
上面使用的登录很简单,但实际情况往往很复杂。明显正常业务需要存的用户信息会要更多。那么我们是否可以扩展身份标识呢?答案是肯定的。
后台代码:
public void Login3(string userName){ ???if (!string.IsNullOrWhiteSpace(userName)) ?//为了方便演示,就不做真的验证了 ????????{ ???????UserInfo user = new UserInfo() ???????{ ???????????Name = userName, ???????????LoginTime = DateTime.Now ???????}; ???????//1、序列化要保存的用户信息 ???????var data = JsonConvert.SerializeObject(user); ???????//2、创建一个FormsAuthenticationTicket,它包含登录名以及额外的用户数据。 ???????FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddDays(1), true, data); ???????//3、加密保存 ???????string cookieValue = FormsAuthentication.Encrypt(ticket); ???????// 4. 根据加密结果创建登录Cookie ???????HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue); ???????cookie.HttpOnly = true; ???????cookie.Secure = FormsAuthentication.RequireSSL; ???????cookie.Domain = FormsAuthentication.CookieDomain; ???????cookie.Path = FormsAuthentication.FormsCookiePath; ???????// 5. 写登录Cookie ???????Response.Cookies.Remove(cookie.Name); ???????Response.Cookies.Add(cookie); ???} ???Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原来页面}
然后在Global.asax的Application_AuthenticateRequest方法:
protected void Application_AuthenticateRequest(){ ???GetUserInfo();}//通过coolie解密 读取用户信息到 HttpContext.Current.Userpublic void GetUserInfo(){ ???// 1. 读登录Cookie ???HttpCookie cookie = Request.Cookies[FormsAuthentication.FormsCookieName]; ???try ???{ ???????UserInfo userData = null; ???????// 2. 解密Cookie值,获取FormsAuthenticationTicket对象 ???????FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value); ???????if (ticket != null && string.IsNullOrEmpty(ticket.UserData) == false) ???????????// 3. 还原用户数据 ???????????userData = JsonConvert.DeserializeObject<UserInfo>(ticket.UserData); ???????if (ticket != null && userData != null) ???????????// 4. 构造我们的MyFormsPrincipal实例,重新给context.User赋值。 ???????????HttpContext.Current.User = new MyFormsPrincipal<UserInfo>(ticket, userData); ???} ???catch { /* 有异常也不要抛出,防止攻击者试探。 */ }}
前端代码:
@{ ???MyFormsPrincipal<UserInfo> user = Context.User as MyFormsPrincipal<UserInfo>; ???if (user == null) ???{ ???????<form action="/home/login3"> ???????????<input type="text" name="userName" /> ???????????<input type="submit" value="登录" /> ???????</form> ???} ???else ???{ ???????<form action="/home/logout2"> ???????????<div>当前用户已登录,登录名:@Context.User.Identity.Name</div> ???????????<div>当前用户已登录,登录时间:@user.UserData.LoginTime</div> ???????????<input type="submit" value="退出" /> ???????</form> ???}}
其实整个过程和FormsAuthentication.SetAuthCookie(userName, true); //登录
是等效的。只是我们通过扩展,存了我们想要存储的数据。
过程也比较简单:
- 构造要存储的数据
- 序列化
- 把序列化信息放入FormsAuthenticationTicket对象
- 通过FormsAuthentication.Encrypt加密对象
- 发送cookie到浏览器
这里稍微复杂点的地方就是解密然后给User赋值HttpContext.Current.User = new MyFormsPrincipal<UserInfo>(ticket, userData);
。
MyFormsPrincipal需要实现接口MyFormsPrincipal
public class MyFormsPrincipal<TUserData> : IPrincipal where TUserData : class, new(){ ???private IIdentity _identity; ???private TUserData _userData; ???public MyFormsPrincipal(FormsAuthenticationTicket ticket, TUserData userData) ???{ ???????if (ticket == null) ???????????throw new ArgumentNullException("ticket"); ???????if (userData == null) ???????????throw new ArgumentNullException("userData"); ???????_identity = new FormsIdentity(ticket); ???????_userData = userData; ???} ???public TUserData UserData ???{ ???????get { return _userData; } ???} ???public IIdentity Identity ???{ ???????get { return _identity; } ???} ???public bool IsInRole(string role)//这里暂时不实现 ???{ ???????return false; ???}}
倒也没有什么特别,就是实例化的时候传入票据和自定义数据就好了。
授权
有了登录一般都离不开授权。微软的东西好就好在,一般都是成套成套的。
[Authorize]public ActionResult LoginOk(){ ???return View();}
直接给Action添加一个Authorize特性就好了,这人就会自动检查是否登录。如果没有登录自动跳转到登录页面。登录页面的设置还是在web.config里面
<system.web> ?<authentication mode="Forms" > ???<forms loginUrl="/home/index"></forms>
这种简单的授权验证明显是不够的。很多时候某些页面只有某些人才能访问。比如VIP。那么我们又要扩展了。
//继承 AuthorizeAttributepublic class MyAuthorizeAttribute : AuthorizeAttribute{ ???public override void OnAuthorization(AuthorizationContext filterContext) ???{ ???????if (filterContext.HttpContext.User.Identity.Name != "农码一生") ???????{ ???????????filterContext.HttpContext.Response.Write("您不是vip用户,不能访问机密数据"); ???????????filterContext.HttpContext.Response.End(); ???????????return; ???????} ???????base.OnAuthorization(filterContext); ???}}
[MyAuthorize]public ActionResult LoginVIP(){ ???return View();}
是的,就是这么简单。说了这么多,来张效果图吧:
推荐阅读:
- http://www.cnblogs.com/fish-li/archive/2012/04/15/2450571.html
Demo: - https://github.com/zhaopeiym/BlogDemoCode/tree/master/权限管理/1-Forms身份认证
【转】权限管理学习 一、ASP.NET FORMS身份认证
原文地址:https://www.cnblogs.com/cjm123/p/8271206.html