一些基本概念:
验证(Authentication)指的是确定用户身份的过程,而授权(Authorization)指的是经过上面的过程之后给予用户访问特定资源的权限;说明白一点,验证就是知道"你是谁",而授权则是"让你可以做什么"
.NET为实现这两个过程提供了Principal和Identity对象。其中,基于角色的安全性基础建立在Principal对象之上,该对象封装了当前用户的信息,既包含用户身份,也包含他所扮演的角色;用户身份用Identity对象来指明,Identity对象中不仅包含指定的用户身份信息(用户名称或账号),还包括了"如何验证这一身份"的方法
Identity对象是实现了IIdentity接口的类的实例。IIdentity接口包括三个只读属性:
string AuthenticationType(get;} 获取所使用的身份验证的类型
bool IsAuthenticated{get;} 登录用户是否经过验证
string Name {get;} 获取当前用户的名称
GenericIdentity类
GenericIdentity类其实相当简单,它并不与任何特定的验证协议相关联。因此,它往往被用在采用了自定义登陆机制的场合。比如一个程序可以自己提示用户输入用户名和密码,然后到自定义的用户数据库中去查询。假如用户名和密码有效,那么程序就会创建一个基于数据库中的匹配记录的principal和(对应的)identity对象。
GenericIdentity类除了三个IIdentity接口定义的属性之外没有更多的东西了。不过,GenericIdentity类提供了两个构造函数。一个构造函数接受一个字符串参数,该参数指定的是用户名;另一个构造函数接受两个参数:第一个是用户名字符串,第二个是给定的验证类型字符串。
public GenericIdentity(string name);
public GenericIdentity(string name, string type);
Principal对象是实现了IPrincipal接口的类的实例,这些对象用来表示用户,并且包括了用户的身份信息。System.Security.Principal命名空间包括了几种类型的Principal类,这些类中封装了程序代码运行的的安全环境(security context)
对于每一个线程来说都与一个principal对象相关联。这个principal对象包括了表示运行当前线程的用户的identity对象。我们可以利用Thread类的静态属性CurrentPrincipal来获得这个principal对象。
下面我们来看看IPrincipal接口,该接口只有一个Identity公共属性和IsInRole公共方法:
1、Identity属性指向一个与principal 对象关联的IIdentity对象。
2、IsInRole方法需要一个字符串参数,该字符串是一个角色的名称,并且返回布尔值,指出principal对象是否属于指定的角色。
GenericPrincipal类
GenericPrincipal类用来表示一个通过自定义验证的用户,通常与GenericIdentity类一起使用。下面是一段简单的程序,说明了这两个类如何使用:
//创建一个GenericIdentity对象
IIdentity myGenericIdentity = new GenericIdentity(strUserName, "MyAuthenticationType");
//创建一个GenericPrincipal对象
String[] roles = null;
GenericPrincipal myGenericPrincipal = new GenericPrincipal(myGenericIdentity, roles);
//将创建的GenericPrincipal对象附加到当前线程上
Thread.CurrentPrincipal = myGenericPrincipal;
注重在上面的例子中,我们可以把MyAuthenticationType的验证类型换成熟知的Kerberos身份验证或者NTLM身份验证。
//下面是验证的过程:
//取得当前线程的principal对象
IPrincipal principal = Thread.CurrentPrincipal;
if (!principal.Identity.Name.Equals("TrustedUser"))
{
throw new SecurityException(
strUserName + " NOT PERMITTED to proceed.\n");
}
Console.WriteLine(
strUserName + " is PERMITTED to proceed.\n");
step 1: 自定义一个MyLoginCommand类并继承于FluorineFx.Security.GenericLoginCommand(此类实现了ILoginCommand接口)基类,并重写DoAuthentication方法
using System; using System.Collections.Generic; using System.Text; using System.Collections; using System.Security.Principal; using FluorineFx.Security; namespace ServiceLibrary8 {public class
MyLoginCommand : GenericLoginCommand {public
override IPrincipal DoAuthentication(string username, Hashtable credentials) { string password = credentials["password"
].ToString(); if (username =="admin"
&& password =="admin"
) { GenericIdentity identity =new
GenericIdentity(username); GenericPrincipal principal =new
GenericPrincipal(identity,new
string[] {"admin"
,"privilegeduser"
});return
principal; } else {//To get this error message as "faultString" thow a SecurityException with the message
//However the first CommandMessage will mess up the result (faultString>faultDetail), and only for subsequent RemotingMessages will work.
//throw new SecurityException("Not a valid username or password");
return
null; } } } }
step 2: 定义一个远程服务接口MyLoginService
using System; using System.Collections.Generic; using System.Text; using FluorineFx; namespace ServiceLibrary8 {/// <summary>
/// MyLoginService is used to force setCredentials sending out credentials
/// For Flash this is also used to log out.
/// MyLoginService被用来强制使得(Flex的)setCredentials发送验证信息
/// 对于flash它也可以用来实现注销登录
/// </summary>
[RemotingService]public class
MyLoginService {public
MyLoginService() { }public
bool Login() {returntrue
; }public
bool Logout() {//FormsAuthentication.SignOut();new
MyLoginCommand().Logout(null);returntrue
; } } }
step 3: 定义实际业务操作类SecureService(经过验证后才能访问到的类)
using System; using FluorineFx; namespace ServiceLibrary8 {/// <summary>
/// Summary description for SecureService
/// </summary>
[RemotingService()]public class
SecureService {public
SecureService() { }public
string GetSecureData() {return"Secure data sent from server."
; } } }
step 4: 配置services-config.xml,确保有以下节点内容:
<security-constraint id="privileged-users"
> <auth-method>Custom</auth-method> <roles> <role>admin</role> <role>privilegeduser</role> </roles> </security-constraint> <login-commandclass
="ServiceLibrary8.MyLoginCommand"
server="asp.net"
/>
step 5: 配置remoting-config.xml,确保有以下节点内容:
<destination id="fluorine"
> <properties> <source>*</source> </properties> <security> <security-constraint ref="privileged-users"
/> </security> </destination> <destination id="login"
> <properties> <source>ServiceLibrary8.MyLoginService</source> </properties> </destination>
step 6: 新建Flex项目,并编写如下代码:
<?xml version="1.0"
encoding="utf-8"
?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
> <mx:Script> <![CDATA[import
mx.rpc.events.ResultEvent;import
mx.utils.ObjectUtil;import
mx.controls.Alert;import
mx.rpc.events.FaultEvent;/* Call the login service*/private function
logIn():void
{//use remote object method setCredentials to//enable Flex to send the values required
loginRO.logout(); loginRO.setCredentials(username.text, password.text); loginRO.Login(); }/*Call logout*/private function
logOut():void
{ loginRO.logout(); appViews.selectedChild = loginView; }private function
getSecureData():void
{ fluorineRO.GetSecureData(); }/* Display error messages returned */private function
serverFault(event:FaultEvent):void
{ Alert.show( ObjectUtil.toString(event.fault.faultString),"Error"
, mx.controls.Alert.OK ); }private function
loginResult(event:ResultEvent):void
{var
result:Boolean = event.resultas
Boolean; appViews.selectedChild = secureView; }private function
loginFault(event:FaultEvent):void
{//Alert.show("Authentication failed", "Errors", mx.controls.Alert.OK);
Alert.show( ObjectUtil.toString(event.fault),"Authentication failed"
); }private function
getSecureDataResult(event:ResultEvent):void
{var
result:String = event.resultas
String; Alert.show(result,"Secure data"
, mx.controls.Alert.OK); } ]]> </mx:Script> <mx:ViewStack id="appViews"
width="100%"
height="100%"
> <!--login view--> <mx:HBox horizontalAlign="center"
verticalAlign="middle"
id="loginView"
width="100%"
height="100%"
> <mx:Panel title="Login"
id="loginPanel"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
> <mx:Form id="loginForm"
> <mx:FormItem label="Username:"
> <mx:TextInput id="username"
/> </mx:FormItem> <mx:FormItem label="Password:"
> <mx:TextInput id="password"
displayAsPassword="true"
/> </mx:FormItem> </mx:Form> <mx:ControlBar> <mx:Spacer width="100%"
id="spacer1"
/> <mx:Button label="Login"
id="loginButton"
enabled="{(username.text.length == 0 || password.text.length == 0) ? false : true}"
toolTip="{loginButton.enabled == true ? 'Click to submit' : 'Enter username and password'}"
click="logIn()"
/> </mx:ControlBar> </mx:Panel> </mx:HBox> <!--end loginView--> <!--secure area view, made visible after a successful login--> <mx:HBox id="secureView"
width="100%"
height="100%"
> <mx:ApplicationControlBar dock="true"
paddingTop="0"
paddingBottom="0"
width="100%"
> <mx:Label text="Action:"
/> <mx:Spacer width="5"
/> <mx:Button label="Get"
click="getSecureData()"
/> <mx:Spacer width="5"
/> <mx:Button label="Logout"
click="logOut()"
/> </mx:ApplicationControlBar> </mx:HBox> <!--end secure area view--> </mx:ViewStack> <mx:RemoteObject id="loginRO"
destination="login"
> <mx:method name="Login"
result="loginResult(event)"
fault="loginFault(event)"
/> </mx:RemoteObject> <mx:RemoteObject id="fluorineRO"
destination="fluorine"
source="ServiceLibrary8.SecureService"
> <mx:method name="GetSecureData"
result="getSecureDataResult(event)"
fault="serverFault(event)"
/> </mx:RemoteObject> </mx:Application>
运行项目,只有用"admin"账户成功登陆,才能访问到来自SecureService的数据