说明:本文示例使用的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身份认证

    [转]权限管理学习 一.ASP.NET Forms身份认证 说明:本文示例使用的VS2017和MVC5. 系统无论大小.牛逼或屌丝,一般都离不开注册.登录.那么接下来我们就来分析下用户身份认证. 简单 ...

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

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

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

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

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

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

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

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

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

    阅读目录 开始 ASP.NET身份认证基础 ASP.NET身份认证过程 如何实现登录与注销 保护受限制的页面 登录页不能正常显示的问题 认识Forms身份认证 理解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. post和get方式区别

    Ajax中我们经常用到get和post请求.那么什么时候用get请求,什么时候用post方式请求呢? 在做回答前我们首先要了解get和post的区别.1. get是把参数数据队列加到提交表单的ACTI ...

  2. Qt开发陷阱一QSTACKWIDGET

    原始日期:2015-10-14 00:55 1.使用QStackWidget控件的setCurrentIndex方法时,要注意参数0对应着ui上StackWidget的page1,而不是page0,没 ...

  3. AVAudioSession(2):定义一个 Audio Session

    本文转自:AVAudioSession(2):定义一个 Audio Session | www.samirchen.com 本文内容主要来源于 Defining an Audio Session. A ...

  4. 《基于Node.js实现简易聊天室系列之详细设计》

    一个完整的项目基本分为三个部分:前端.后台和数据库.依照软件工程的理论知识,应该依次按照以下几个步骤:需求分析.概要设计.详细设计.编码.测试等.由于缺乏相关知识的储备,导致这个Demo系列的文章层次 ...

  5. Array.apply(null,{length:20})与new Array(20)的区别

    Array.apply(null,{length:20}) 这句代码的实际意义:创建长度为20的一个数组,但并非空数组. 跟new Array(20)的区别在于,前一种创建方式,得到的数组中的每一个元 ...

  6. 大话Session

    [原创]转载请保留出处:shoru.cnblogs.com 晋哥哥的私房钱 引言 在web开发中,session是个非常重要的概念.在许多动态网站的开发者看来,session就是一个变量,而且其表现像 ...

  7. 解决yii框架,gii脚手架不能使用。

    应用场景 把代码转移到线上服务器时,GII.BUG工具不正常使用,但在本地服务器是正常的. 分析原因 Yii框架在使用GII 和BUG 时,会针对访问IP地址拦截,没有在配置中设置的IP地址是会默认被 ...

  8. JAVA数字求和

    设计思想:把子符串转换成数字,通过Integer.parseInt(),然后通过循环求和. 流程图:

  9. Openfire插件开发图解

    概述 Openfire插件开发是Openfire的精髓之一,支持插件热插拔,还可以方便的在web端进行管理插件.插件分为两种,一种是以服务为主的控制台插件,一种是包括页面或对外开放Servlet接口. ...

  10. Dagger2在Android开发中的应用

    世界是普遍联系的,任何事物和个体都直接或间接相互依赖,在时空长河中共同发展.在面向对象的世界中,更是如此,类与类之间的依赖,关联关系,模块(亦或是分层架构中的层)之间的耦合关系,都是我们在软件开发实践 ...