【转】权限管理学习 一、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.User
public 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。那么我们又要扩展了。

//继承 AuthorizeAttribute
public 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();
}

是的,就是这么简单。说了这么多,来张效果图吧:

推荐阅读:

【转】权限管理学习 一、ASP.NET Forms身份认证的更多相关文章

  1. 简单的ASP.NET Forms身份认证

    读了几篇牛人的此方面的文章,自己也动手做了一下,就想有必要总结一下.当然我的文章质量自然不能与人家相比,只是写给从没有接触过这个知识点的朋友. 网站的身份认证我以前只知道session,偶然发现一些牛 ...

  2. 细说ASP.NET Forms身份认证

    阅读目录 开始 ASP.NET身份认证基础 ASP.NET身份认证过程 如何实现登录与注销 保护受限制的页面 登录页不能正常显示的问题 认识Forms身份认证 理解Forms身份认证 实现自定义的身份 ...

  3. IE11下ASP.NET Forms身份认证无法保存Cookie的问题

    IE11下ASP.NET Forms身份认证无法保存Cookie的问题 折腾了三四天,今天才找到资料,解决了. 以下会转贴,还没来得及深究,先放着,有空再学习下. ASP.NET中使用Forms身份认 ...

  4. ASP.NET Forms身份认证详解

    ASP.NET身份认证基础 在开始今天的内容之前,我想有二个最基础的问题首先要明确: 1. 如何判断当前请求是一个已登录用户发起的? 2. 如何获取当前登录用户的登录名? 在标准的ASP.NET身份认 ...

  5. 细说ASP.NET Forms身份认证 别人写的不过很透彻就转来了以后用时再看

    阅读目录 开始 ASP.NET身份认证基础 ASP.NET身份认证过程 如何实现登录与注销 保护受限制的页面 登录页不能正常显示的问题 认识Forms身份认证 理解Forms身份认证 实现自定义的身份 ...

  6. 权限管理学习 一、ASP.NET Forms身份认证

    说明:本文示例使用的VS2017和MVC5. 系统无论大小.牛逼或屌丝,一般都离不开注册.登录.那么接下来我们就来分析下用户身份认证. 简单实现登录.注销 以前在学习.net的时候不知道什么Forms ...

  7. 关于Asp.Net Forms身份认证

    Asp.Net管道式的构建个我们提供了通过IHttpMoudle来订阅管线事件来达到干预HTTP请求的目的,Asp.Net的身份认证正是通过此种方式来对请求来执行身份认证的,这篇文章仅仅谈论Forms ...

  8. C# ASP.NET Forms身份认证

    原文:https://www.cnblogs.com/kyo-lynn/p/3418577.html 原文:https://www.cnblogs.com/fish-li/archive/2012/0 ...

  9. ASP.NET Forms身份认证

    asp.net程序开发,用户根据角色访问对应页面以及功能. 项目结构如下图: 根目录 Web.config 代码: <?xml version="1.0" encoding= ...

随机推荐

  1. c# 抽象类 抽象函数 接口

    抽象类与抽象方法: 被abstract关键字修饰的类叫做抽象类 被abstract关键字修饰的方法叫做抽象方法 1.抽象方法必须放在抽象类中 2.抽象方法不可以实现代码,用空语句替代 3.抽象方法可以 ...

  2. Lepus监控之SQLServer配置(后续整理)

    1.安装pymssql模块 1-1.环境准备: 1-1-1.unixODBC安装 yum install unixODBC unixODBC-devel -y 1-1-2.freetds安装下载 fr ...

  3. python网页爬虫开发之四-串行爬虫代码示例

    实现功能:代理.限速.深度.反爬 import re import queue import urllib.parse import urllib.robotparser import time fr ...

  4. 【亲测】502 Bad Gateway 怎么解决?

    502 Bad Gateway 怎么解决? 1.什么是502 badgateway 报错 简单来说 502 是报错类型代码,bad gateway 错误的网关. 2.产生错误的原因 连接超时 具体原因 ...

  5. VS 代码片段集

    片段1:用于线程执行代码,耗时操作时加载Loging; <?xml version="1.0" encoding="utf-8"?> <Cod ...

  6. js获取浏览器内核判断终端(是QQ打开还是QQ浏览器打开)

    var browser ={ versions: function() { var u = navigator.userAgent, var ua = navigator.userAgent.toLo ...

  7. hadoop的hdfs中的javaAPI操作

    package cn.itcast.bigdata.hdfs; import java.net.URI; import java.util.Iterator; import java.util.Map ...

  8. CentOS6.8 下RPM方式安装MySQL5.6

    1. 检查MySQL及相关RPM包,是否安装,如果有安装,则移除(rpm –e 名称) yum remove mysql mysql-server mysql-libs(我用的上面的)或者 [root ...

  9. 触发full gc的条件

    1.调用System.gc 2.老年代空间不足 3.永生区空间不足 4.CMS GC时出现promotion failed和concurrent mode failure 5.统计得到的Minor G ...

  10. 【392】Python 列表解析

    参考: Python3 数据结构 | 菜鸟教程 列表推导式 列表推导式提供了从序列创建列表的简单途径.通常应用程序将一些操作应用于某个序列的每个元素,用其获得的结果作为生成新列表的元素,或者根据确定的 ...