[转].net中的认证(authentication)与授权(authorization)

本文转自:http://www.cnblogs.com/yjmyzz/archive/2010/08/29/1812038.html

注:这篇文章主要给新手看的,老手们可能会觉得没啥营养,就请绕过吧。

“认证”与“授权”是几乎所有系统中都会涉及的概念,通俗点讲:

认证(authentication) 就是
"判断用户有没有登录?",好比windows系统,没登录就无法使用(不管你是用Administrator或Guest用户,总之要先正确登录后,才能进入系统).

授权(authorization)
就是"用户登录后的身份/角色识别",好比"管理员用户"登录windows后,能安装软件、修改windows设置等所有操作,而Guest用户登录后,只有做有限的操作(比如安装软件就被禁止了).

.net中与"认证"对应的是IIdentity接口,而与"授权"对应的则是IPrincipal接口,这二个接口的定义均在命名空间System.Security.Principal中:

+ View
Code
?





1

2

3

4

5

6

7

8

9

10

11

12

13

using
System;

using
System.Runtime.InteropServices;

namespace
System.Security.Principal

{

    [ComVisible(true)]

    public
interface IIdentity

    {

           string
AuthenticationType { get; }

           bool
IsAuthenticated { get; }

           string
Name { get; }

    }

}

+ View
Code
?





1

2

3

4

5

6

7

8

9

10

11

12

using
System;

using
System.Runtime.InteropServices;

namespace
System.Security.Principal

{

    [ComVisible(true)]

    public
interface IPrincipal

    {

          IIdentity Identity { get; }

          bool
IsInRole(string
role);

    }

}

应该注意到:IPrincipal接口中包含着一个只读的IIdentity,这也跟最开始提到的概念一致:识别身份的前提是先登录,只有登录成功后能进一步确认身份。

用Membership/Role做过asp.net开发的朋友们,看到这二个接口的定义,应该会觉得很眼熟,想想我们在Asp.Net页面中是如何判断用户是否登录以及角色的?

+ View
Code
?





1

2

3

4

5

6

7

8

9

10

11

12

protected
void Page_Load(object
sender, EventArgs e)

        {

            HttpContext ctx = HttpContext.Current;

            if
(ctx.User.Identity.IsAuthenticated && ctx.User.IsInRole("管理员"))

            {

                //管理员该做的事,就写在这里

            }

            else

            {

                //Hi,您不是管理员,别胡来!

            }

        }

这段代码再熟悉不过了,没错!membership/role的原理就是基于这二个接口的,如果再对HttpContext.Current.User刨根问底,能发现下面的定义:

即:HttpContext.Current.User本身就是一个IPrincipal接口的实例。有了上面的预备知识,可以直奔主题了,先来一个Console控制台程序测试一下用法:

+ View
Code
?





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

using
System;

using
System.Security.Principal;

using
System.Threading;

namespace
ConsoleTest

{

    class
Program

    {

        static
void Main(string[] args)

        {

            GenericIdentity _identity = new
GenericIdentity("菩提树下的杨过");

            GenericPrincipal _principal = new
GenericPrincipal(_identity, new
string[] {"管理员","网站会员"
});

            Thread.CurrentPrincipal = _principal;//并非必需,但在winform程序中有很用(后面会提到)

            string
loginName = _principal.Identity.Name;

            bool
isLogin = _principal.Identity.IsAuthenticated;

            bool
isAdmin = _principal.IsInRole("管理员");

            bool
isWebUser = _principal.IsInRole("网站会员");

            Console.WriteLine("当前用户: {0}", loginName);

            Console.WriteLine("是否已经登录? {0}", isLogin);

            Console.WriteLine("是否管理员? {0}", isAdmin);

            Console.WriteLine("是否网站会员? {0}", isWebUser);

            Console.Read();           

        }

    }

}

输出如下:

当前用户: 菩提树下的杨过 是否已经登录? True 是否管理员?
True
是否网站会员?
True

一切正常,没什么大不了,但Console默认只是一个单线程的程序,也没有丰富的GUI界面,所以...这个只不过是热身,看下接口定义的几个方法是否管用而已。

这二个接口同样也能用在Winform程序中,下面将创建一个WinForm应用,里面有二个窗口:Form1以及Form2,可以把Form1当成登录界面,而Form2则是程序主窗口,在很多管理软件中,主窗口都要求登录以后才能访问,我们就来模拟一下:

Form1的界面:

Form2更简单:(就一个只读的TextBox)

我想做的事情:在Form1上登录后,看看在Form2中,能否判断出用户已经登录,以及识别出身份。

Form1 中的代码:

+ View
Code
?





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

31

32

33

34

35

36

37

38

39

40

41

42

using
System;

using
System.Security.Principal;

using
System.Threading;

using
System.Windows.Forms;

namespace
WinformTest

{

    public
partial class Form1 : Form

    {

        public
Form1()

        {

            InitializeComponent();

        }

        private
void btnLogin_Click(object
sender, EventArgs e)

        {

            if
(txtUserName.Text.Trim() == "") {

                MessageBox.Show("请输入用户名!");

                txtUserName.Focus();

                return;

            }

            IIdentity _identity = new
GenericIdentity(txtUserName.Text.Trim());

            IPrincipal _principal = new
GenericPrincipal(_identity, new
string[] { "管理员"
});

            Thread.CurrentPrincipal = _principal;//将其附加到当前线程的CurrentPrincipal

            MessageBox.Show("登录成功!");

        }

        private
void btnShow_Click(object
sender, EventArgs e)

        {

            (new
Form2()).ShowDialog();

        }

        private
void btnLogOut_Click(object
sender, EventArgs e)

        {

            Thread.CurrentPrincipal = null;

            MessageBox.Show("已经退出!");

        }

    }

}

Form2中的代码:

+ View
Code
?





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

using
System;

using
System.Security.Principal;

using
System.Threading;

using
System.Windows.Forms;

namespace
WinformTest

{

    public
partial class Form2 : Form

    {

        public
Form2()

        {

            InitializeComponent();

        }

        private
void Form2_Load(object
sender, EventArgs e)

        {

            IPrincipal _principal = Thread.CurrentPrincipal;

            if
(_principal.Identity.IsAuthenticated)

            {

                this.textBox1.Text = "您已经登录,当前用户:"
+ _principal.Identity.Name;

                this.textBox1.Text += Environment.NewLine + "当前角色:"
+ (_principal.IsInRole("管理员") ? "管理员"
: "非管理员");

            }

            else

            {

                this.textBox1.Text = "您还没有登录";

            }

        }

    }

}

测试一下:如果在未登录的情况下,直接点击"Show窗体2",结果如下

如果输入用户名,并点击"登录"后,再点击"Show窗体2",结果如下:

很理想!Form2中直接就能判断用户是否登录,以及当前登录用户的角色。这里有一个关键的细节:

?





1

Thread.CurrentPrincipal = _principal;//将其附加到当前线程的CurrentPrincipal

在Form1中,将登录后的_principal附加到当前线程的CurrentPrincipal,我们知道:每个程序不管它是不是多线程,总归是有一个默认的主线程的。所以只要把主线程的CurrentPrincipal与登录后的_principal关联起来后,其它任何窗体,都可以直接用它来做判断,如果判断通过,则可以这样或那样(包括创建多线程进行自己的处理),如果判断不通过,则可以拒绝继续操作。

Winform的问题解决了,再来考虑一下Webform,当然,你可以直接使用从Asp.Net2.0就支持的membership/role机制,但membership/role默认只支持sqlserver数据库(通过membership
provider for
oracle也可以支持oracle,但总有一些数据库不被支持,比如access、mysql、sqlite、db2等),假如你不想把用户名/密码这类信息保存在sqlserver中(甚至不想保存在数据库中,比如:xml),这时候就得开动脑筋了。

其实...就算不用membership/role,上面提到的这二个接口仍然是可以使用的,但有一个问题:winform中,IPrincipal接口的实例可以一直存储在内存中(直到程序退出),所以其它窗口就能继续访问它,以便做进一步的判断,但是在webform中,页面本身是无状态的,一旦服务器输出html到客户端浏览器后,客户端的页面就与服务器再无瓜葛了(你甚至可以离线浏览,前提是不刷新),那么最后的认证信息保存在什么地方呢?

答案就是客户端的浏览器Cookie!所以在WebForm中的做法稍有不同:

创建一个webApplication,里面新建4个页面:login.aspx,logout.aspx,default.aspx,gotoUrl.aspx,这四个页面的作用如下:

login.aspx : 登录页面

logout.aspx: 用来处理用户注销 (非必需,但建议把注销逻辑放在这里,以便任何需要注销的地方重复利用)

default.aspx: 登录完成后的显示页面

gotoUrl.aspx : 登录完成后,用来辅助做页面跳转的页面(非必需,但建议加上)

login.aspx代码:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="LoginTest.Login" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<table>
<tr>
<td>用户名:</td>
<td>
<asp:TextBox ID="txtUserName" runat="server" style="width:200px"></asp:TextBox></td>
</tr>
<tr>
<td>密  码:</td>
<td>
<asp:TextBox ID="txtPassword" runat="server" TextMode="Password" style="width:200px"></asp:TextBox>
</td>
</tr>
<tr>
<td></td>
<td>
<asp:Button ID="Button1" runat="server" Text="登 录" onclick="Button1_Click" />
</td>
</tr>
</table>
</form>
</body>
</html>

后置代码:

using System;
using System.Web;
using System.Web.Security;

namespace LoginTest
{
public partial class Login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

}

protected void Button1_Click(object sender, EventArgs e)
{
string user = this.txtUserName.Text; //读取用户名
string password = this.txtPassword.Text; //读取密码
if (ValidateUser(user, password) == true) //ValidateUser方法用来验证用户合法性的
{
//建立表单验证票据
FormsAuthenticationTicket Ticket = new FormsAuthenticationTicket(1, user, DateTime.Now, DateTime.Now.AddMinutes(30), true, "管理员,会员", "/");

//使用webcongfi中定义的方式,加密序列化票据为字符串
string HashTicket = FormsAuthentication.Encrypt(Ticket);

//将加密后的票据转化成cookie
HttpCookie UserCookie = new HttpCookie(FormsAuthentication.FormsCookieName, HashTicket);

//添加到客户端cookie
Context.Response.Cookies.Add(UserCookie);

//登录成功后重定向
Response.Redirect("GotoUrl.aspx?returnUrl=" + Server.UrlEncode("Default.aspx"));
}
else
{
//登录失败后的处理
}
}

/// <summary>
/// 验证用户名/密码是否正确
/// </summary>
/// <param name="userName"></param>
/// <param name="pwd"></param>
/// <returns></returns>
private bool ValidateUser(string userName, string pwd) {
return true; //当然实际开发中,您可以到数据库里查询校验,这里只是示例而已
}
}
}

GotoUrl.aspx:这个页面只是单纯的辅助跳转而已,所以aspx页面本身不用加什么代码,只需要在后置cs代码里简单处理一下

using System;

namespace LoginTest
{
public partial class GotoUrl : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string _returnUrl = Request["returnUrl"];

if (string.IsNullOrEmpty(_returnUrl))
{
_returnUrl = "~/default.aspx";
}

Response.Redirect(_returnUrl);
}
}
}

接下来应该是Default.aspx了,这里只是演示,所以没有后置代码,判断的逻辑全写在default.aspx本身:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="LoginTest.Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<% if (User.Identity.IsAuthenticated)
{
Response.Write("<span style=‘color:red‘>" + User.Identity.Name + "</span>已登录!");
if (User.IsInRole("管理员"))
{
Response.Write(" 当前用户角色:管理员");
}

if (User.IsInRole("会员"))
{
Response.Write(",会员。");
}

Response.Write(" <a href=‘logout.aspx‘>安全退出</a>");
}
else
{
Response.Write("请先<a href=‘login.aspx‘>登录</a>");
}
%>
</div>
</form>
</body>
</html>

最后一个是注销页面logout.aspx,类似的,这个页面本身只负责注销cookie票据,所以界面上没东西,只有后置代码:

using System;
using System.Web.Security;

namespace LoginTest
{
public partial class Logout : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
FormsAuthentication.SignOut();
Response.Redirect("default.aspx");
}
}
}

如果您已经等不急的按下了F5想看下最终的结果,可能会令人失望:

咱还没登录呢,甚至连用户名,密码都没输入,咋会显示已登录?是不是想起了小沈阳的那句经典台词:为~什么呢?

这就是webform与winform不同的地方,asp.net默认的表单认证方式是Windows,所以程序一运行,asp.net就把windows当前的登录用户视为已经登录了,因此我们得改变asp.net的默认“傻帽”行为,修改web.config成下面这样:

<?xml version="1.0"?>

<configuration>
<system.web>

<compilation debug="true" targetFramework="4.0" />

<authentication mode="Forms">
<forms
name=".ASPXAUTH"
loginUrl="login.aspx"
timeout="30"
path="/"
requireSSL="false"
domain="">
</forms>
</authentication>

</system.web>

</configuration>

哦,忘了告诉大家,我用的是asp.net 4.0,所以web.config显示十分简洁清爽。

ok,再来跑一下:

这回对了,点击“登录",转到login.aspx,然后在用户名里输入点啥(比如:"菩提树下的杨过"),然后会得到下面的结果:

认证已经成功了!但是好象还有点问题:并没有识别出身份!(即login.aspx.cs中代码指定的"管理员,会员"角色)

静下心来想想问题出在哪里?

在winform中,我们用

IPrincipal _principal = new GenericPrincipal(_identity, new string[] { "管理员" });
Thread.CurrentPrincipal = _principal;//将其附加到当前线程的CurrentPrincipal

给_principal授权为"管理员"(当然还能给它更多的角色),然后将其赋值为线程的CurrentPrincipal,所以就ok了,但是webform中并没有Thread.CurrentPrincipal,而且http本身又是无状态的,下一次http请求,根本无法记得上次请求时的状态(就好象每次http请求都是重新投胎一样,前世忘记得一干二净),幸好:微软为asp.net搞出一个上下文Context的概念,一个webApplication中,虽然http协议本身是无状态的,但是每个aspx页面被请求时,总会附带一个HttpContext上下文,可以用它来找回一些前世的记忆,而且文章最开头提到了
HttpContext.Current.User本身就是IPrincipal,这不就是Thread.CurrentPrincipal的变种么?

顺便再回忆一下Asp.Net的页面生命周期,每个AspX页面在请求认证时,都会触发Application_AuthenticateRequest事件,而这个事件是定义在Global.ascx中的,所以可以从这个入手:

新建一个Global.ascx,打开后置代码,内容如下:

using System;
using System.Security.Principal;
using System.Web;
using System.Web.Security;

namespace LoginTest
{
public class Global : System.Web.HttpApplication
{

protected void Application_Start(object sender, EventArgs e)
{

}

protected void Session_Start(object sender, EventArgs e)
{

}

protected void Application_BeginRequest(object sender, EventArgs e)
{

}

/// <summary>
/// 每个aspx页面要求认证时被触发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
HttpContext _ctx = HttpContext.Current;
if (_ctx.User != null)
{
if (_ctx.User.Identity.IsAuthenticated == true) //认证成功的用户,才进行授权处理
{
FormsIdentity _Identity = (FormsIdentity)_ctx.User.Identity;
string[] Roles = _Identity.Ticket.UserData.Split(‘,‘); //将角色字符串,即login.aspx.cs中的“管理员,会员”,变成数组
_ctx.User = new GenericPrincipal(_Identity, Roles); //将带有角色的信息,重新生成一个GenericPrincipal赋值给User,相当于winform中的Thread.CurrentPrincipal = _principal
}
}

}

protected void Application_Error(object sender, EventArgs e)
{

}

protected void Session_End(object sender, EventArgs e)
{

}

protected void Application_End(object sender, EventArgs e)
{

}
}
}

再测试一下,结果总算正常了:

最后再帮.Net做点广告:.Net是一个平台,其中的很多技术是全平台通用的(不管是winform还是webform),强烈建议大家尽量向微软自带的标准模型靠拢,这样在多种不同类型的应用整合时,将非常方便,而且兼容性好,容易升级。

经常看见有人winform中登录用一种做法(比如设置一个全局的静态变量,判断用户是否已经登录),然后webform中又动不少脑筋想一种做法(比如自己建用户表,搞加密算法,然后用session做判断),假如以后这二种应用要整合起来,估计要费不少劲(当然,也有设计得很好,一开始就考虑到日后的扩展的,但是这种毕竟少数,而且相对而言,对程序员的要求比较高),但是如果大家都用文中所提的标准模型(IIdentity,IPrincipal),要整合这二种应用是非常方便的。

[转].net中的认证(authentication)与授权(authorization),布布扣,bubuko.com

时间: 2024-10-05 04:09:22

[转].net中的认证(authentication)与授权(authorization)的相关文章

.net中的认证(authentication)与授权(authorization)

本文转自:http://www.cnblogs.com/yjmyzz/archive/2010/08/29/1812038.html 认证(authentication) 就是 "判断用户有没有登录?",好比windows系统,没登录就无法使用(不管你是用Administrator或Guest用户,总之要先正确登录后,才能进入系统). 授权(authorization) 就是"用户登录后的身份/角色识别",好比"管理员用户"登录windows后,

认证 (authentication) 和授权 (authorization) 的区别

authentication 和 authorization是两个比较容易混淆的词.其实很简单,举个例子来说: 你要登机,你需要出示你的身份证和机票,身份证是为了证明你张三确实是你张三,这就是 authentication:而机票是为了证明你张三确实买了票可以上飞机,这就是 authorization. 再举一个计算机领域的例子: 你登录一个系统,输入用户名张三,密码123,密码正确,证明你张三确实是张三,这个过程叫authentication:而你是一个一般用户,只有浏览该系统的权限,而李四是

【智能家居篇】wifi网络接入原理(中)——认证Authentication

转载请注明出处:http://blog.csdn.net/Righthek 谢谢! 还是用手机来举例,扫描完成后,我们会选择想要加入的WIFI热点.此时,大部分都会弹出一个输入密码的窗口,当然也有不用输入密码的.这个过程叫做:认证(Authentication). 有时觉得搞技术的真累,当我们辛辛苦苦地在网上找资料学习新技术时,当我们渐渐掌握这门技术之后,才发现该技术对自己来说是新的,但是对现时来说,已经过时并且被遗弃了!所以说--Shit! 由于网上大量讲解WIFI技术的资料在认证方面都是侧重

在AngularJS应用中实现认证授权

在AngularJS应用中实现认证授权 在每一个严肃的应用中,认证和授权都是非常重要的一个部分.单页应用也不例外.应用并不会将所有的数据和功能都 暴露给所有的用户.用户需要通过认证和授权来查看应用的某个特定部分,或者在应用中进行特定的行为.为了在应用中对用户进行识别,我们需要让用户进行登录. 在用户管理方面,传统的服务器端应用和单页应用的实现方式有所不同,单页应用能够和服务器通信的方式只有AJAX.对于登录和退出来说也是如此. 负责识别用户的服务器端需要暴露出一个认证断电.单页应用将会把用户输入

JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法、shiro认证与shiro授权

shiro介绍 什么是shiro shiro是Apache的一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权.加密.会话管理等功能,组成了一个通用的安全认证框架.它可以实现如下的功能: 1.验证用户 2.对用户执行访问控制,如:判断用户是否具有角色admin,判断用户是否拥有访问的资源权限. 3.在任何环境下使用SessionAPI.例如C/S程序 4.可以使用多个用户数据源.例如一个是Oracle数据库,另外一个是MySQL数据库. 5.单点登录(SSO)功能

Shiro 缓存认证信息和授权信息

spring-shiro.xml文件配置 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="customAuthorizingRealm"/> <property name="cacheManager"

NET Core中的认证管理解析

2016-08-19 07:15 0x00 问题来源 在新建.NET Core的Web项目时选择"使用个人用户账户"就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可以使用AuthorizeAttribute进行各种权限管理,看起来似乎十分方便.不过生成的代码都替我干了些什么我一团雾水.看了下生成的数据表,功能也挺复杂的.实际上我需要的只是基于用户和角色的认证管理,而且用户资料是使用现有的库,但使用.NET Core自带的认证组件必须要依赖EF,表的结构

Linux中pam认证详解(下)

Linux中pam认证详解(下) 在上一篇中详细的介绍了pam的介绍.pam认证原理.pam认证构成以及pam验证控制类型,下面向大家详细介绍一下常用的pam服务模块,以及pam认证的应用. 一.常用的pam服务模块 下面是Linux提供的PAM模块列表(只是其中一部分),这其中包含模块文件.模块功能描述和相关配置文件,具体如下: pam_access 提供logdaemon风格登录控制 /etc/security/access.conf pam_chroot 提供类似chroot命令的功能 p

Django用户认证系统(二)Web请求中的认证

在每个Web请求中都提供一个 request.user 属性来表示当前用户.如果当前用户未登录,则该属性为AnonymousUser的一个实例,反之,则是一个User实例. 你可以通过is_authenticated()来区分,例如: if request.user.is_authenticated(): # Do something for authenticated users. else: # Do something for anonymous users. 登陆login login(