FluorineFX的验证(Authentication)与授权(Authorization)

时间:2022-08-30 10:23:11

一些基本概念:

验证(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()
        {
            
return true
;
        }
        
public
bool Logout()
        {
           
//FormsAuthentication.SignOut(); new
MyLoginCommand().Logout(null);
            
return true
;
        }
    }
}

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-command 
class
=
"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.result  
as
  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.result  
as
  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的数据