FluorineFx:認證與授權

FluorineFx:認證與授權

對認證與授權沒啥概念的新同學,建議先看下 .net中的認證(authentication)與授權(authorization),然後再繼續。

 

Flash/Flex在通過FluorineFx調用.Net中的方法時,同樣也會遇到認證與授權問題,即:

 “是否隨便一個阿貓阿狗都能來調用我的方法?”或者可以理解爲:“調用我的方法前是否需要登錄?” 這就是認證

“門衛放進來後,是不是不管什麼身份的人,都能來拿東西?”或者可以理解爲:“登錄後的用戶,具備何種資格的人才能調用方法?” 這就是授權

 

步驟:

1、先創建自己的LoginCommand類(相當於門衛,用於把關):DemoLoginCommand

01 using System.Collections;
02 using System.Security.Principal;
03 using FluorineFx.Security;
04  
05 namespace _04_Authentication
06 {
07     publicclass DemoLoginCommand:GenericLoginCommand
08     {
09         publicoverride IPrincipal DoAuthentication(stringusername, Hashtable credentials)
10         {
11             stringpassword = credentials["password"]as string;          
12             if(username.Length > 0)//當然:這裏只是演示,實際應用中可從數據庫中檢驗用戶名和密碼
13             {
14                 GenericIdentity identity =new GenericIdentity(username);
15                 //下面的代碼,授予登錄用戶:"admin"角色,仍然只是演示,實際應用中,可以根據用戶名到數據庫裏查詢對應的角色
16                 GenericPrincipal principal =new GenericPrincipal(identity,new string[] {"admin"});
17  
18                 returnprincipal;
19             }
20             else//檢驗不通過的處理
21             {
22                 returnnull;
23             }
24         }
25     }
26 }

2、再創建一個不需要認證就能隨便調用的遠程服務 DemoLoginService

01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Web;
05 using FluorineFx;
06  
07 namespace _04_Authentication
08 {
09     [RemotingService]
10     publicclass DemoLoginService
11     {
12         //其實這個方法隨便起什麼名字都行,只要在flash中調用時跟這裏一致就行(可以理解爲方法佔位符),但是通常爲了有意義,命名爲Login
13         publicbool Login()
14         {
15             //這個返回值,其實返回true或false都無所謂,重點不在這裏
16             returntrue;
17         }
18  
19         //同樣,這裏方法的名字其實也可以隨便起
20         publicbool Logout()
21         {
22             newDemoLoginCommand().Logout(null);//這一行纔是關鍵,相當於把"認證信息"清空了
23             returntrue;//明白了上一行後,其實也應該能想到:這裏返回true或flase其實都不重要
24         }
25     }
26 }

3、爲了對比,再來創建一個需要認證的遠程服務:DemoSecureService

01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Web;
05 using FluorineFx;
06  
07 namespace _04_Authentication
08 {
09     [RemotingService]
10     publicclass DemoSecureService
11     {        
12         //太簡單了,不作解釋了
13         publicstring HelloWorld()
14         {
15             return"從服務端返回的安全數據.";
16         }
17     }
18 }

等一下,提個問題:比較這個服務跟剛纔創建的服務,除了裏面的方法名稱(及相關的方法處理邏輯)不同,本質上沒區別吧? 憑啥說調用這個服務就需要認證,而剛纔那個服務就能隨便調用?

很好,希望大家帶着這個問題繼續,後面會揭曉。

先打個岔:回想一下asp.net中後臺目錄權限的處理,我們可以在web.config 中通過配置來決定某個目錄是否可訪問

1 <locationpath="admin">
2     <system.web>
3         <authorization>
4             <denyusers="?"/>
5             <denyroles="買家"/>
6             <denyroles="賣家"/>
7         </authorization>
8     </system.web>
9 </location>

這段配置的意思就是 /admin目錄,匿名用戶無法訪問(即要求登錄),同時"買家","賣家"二種角色被拒絕了(即:就算你登錄了,只要你是"買家"或"賣家"角色,同樣也訪問不了)

FluorineFx中,同樣也是用配置來實現權限訪問的:

先看remoting-config.xml的配置

01 <?xmlversion="1.0"encoding="UTF-8"?>
02 <serviceid="remoting-service"
03     class="flex.messaging.services.RemotingService"
04     messageTypes="flex.messaging.messages.RemotingMessage">
05  
06  
07   <adapters>
08     <adapter-definitionid="dotnet"class="FluorineFx.Remoting.RemotingAdapter"default="true"/>
09   </adapters>
10  
11   <default-channels>
12     <channelref="my-amf"/>
13   </default-channels>
14  
15  
16  
17   <destinationid="fluorine">
18     <properties>
19       <!--這裏表示所有源(文件)包括在內-->
20       <source>*</source>
21     </properties>
22     <security>
23       <!--這裏表明訪問上面source中定義的服務時,必須滿足"privileged-users"的安全限制-->
24       <security-constraintref="privileged-users"/>
25     </security>
26   </destination>
27  
28  
29   <destinationid="login">
30     <properties>
31       <source>_04_Authentication.DemoLoginService</source>
32       <!--注意這裏沒有<security />節點,即_04_Authentication.DemoLoginService的訪問沒有安全限制,也就是可隨便調用-->
33     </properties>
34   </destination>
35  
36 </service>

關鍵地方已經註釋了,這個配置就表明了_04_Authentication.DemoLoginService不需要登錄就能調用,而其它服務調用時要受到"privileged-users"的限制,那麼這個限制到底如何描述的呢?

services-config.xml配置

01 <?xmlversion="1.0"encoding="utf-8"?>
02 <services-config>
03   <services>
04     <service-includefile-path="remoting-config.xml"/>
05   </services>
06  
07  
08   <security>
09     <!--這裏跟前面的"privileged-users"對應上了-->
10     <security-constraintid="privileged-users">
11       <!--表明認證方法爲開發人員自定義-->
12       <auth-method>Custom</auth-method>
13       <!--調用該限制相關的服務,必須是admin或user角色-->
14       <roles>
15         <role>admin</role>
16         <role>user</role>
17       </roles>
18     </security-constraint>
19     <!--這裏指明瞭驗證登錄時,由誰來把關,即DemoLoginCommand-->
20     <login-commandclass="_04_Authentication.DemoLoginCommand"server="asp.net"/>
21   </security>
22  
23   <channels>
24     <channel-definitionid="my-amf"class="mx.messaging.channels.AMFChannel">
25       <endpointuri="http://{server.name}:{server.port}/{context.root}/Gateway.aspx"class="flex.messaging.endpoints.AMFEndpoint"/>
26     </channel-definition>
27     <properties>
28       <!-- <legacy-collection>true</legacy-collection> -->
29     </properties>
30   </channels>
31 </services-config>

同樣:重點地方已經加了註釋。

另外一個重要配置:fluorineFx說到底是宿主在asp.net iis環境中的,所以它的認證票據同樣是保存在cookie中的,web.config的表單認證方式要設置爲Forms,即

01 <?xmlversion="1.0"?>
02 <configuration>
03   <system.web>
04     <compilationdebug="true"targetFramework="4.0"/>
05  
06     <httpModules>
07       <addname="FluorineGateway"type="FluorineFx.FluorineGateway, FluorineFx"/>
08     </httpModules>
09  
10      <!--這裏要指定爲Forms認證方式-->
11     <authenticationmode="Forms"></authentication>
12      
13   </system.web>
14 </configuration>

ok,服務端就全部完成了,再來看Flash端的調用:

UI界面:

 

先講下我們要做什麼:

a、點擊“登錄”或“註銷”時,調用不需要登錄的DemoLoginService

b、點擊"遠程調用"時,調用需要認證的DemoSecureService

預測一下結果:

點擊“登錄”前,如果直接點擊“遠程調用”,應該會調用失敗(因此此時尚未登錄認證)

如果先點擊“登錄”後,再點擊“遠程調用”,因爲這時已經登錄認證過了,所以應該成功

完整flash代碼:

001 package
002 {
003     importflash.display.Sprite;
004     importflash.net.ObjectEncoding;
005     importorg.bytearray.remoting.Service;
006     importorg.bytearray.remoting.PendingCall;
007     importorg.bytearray.remoting.events.ResultEvent;
008     importorg.bytearray.remoting.events.FaultEvent;
009     importfl.controls.TextInput;
010     importflash.text.TextField;
011     importfl.controls.TextArea;
012     importfl.controls.Button;
013     importflash.events.MouseEvent;
014  
015  
016  
017     publicclass LoginDemo extendsSprite
018     {
019         privatevar _txtUserName:TextInput;
020         privatevar _txtPassWord:TextInput;
021         privatevar _txtResult:TextArea;
022         privatevar _btnLogin:Button;
023         privatevar _btnLogOut:Button;
024         privatevar _btnCall:Button;
025  
026         privatevar _gatewayUrl:String= "";
027         privatevar _service:Service;      
028         privatevar _callService:Service;
029         privatevar _rpc:PendingCall;
030          
031  
032         publicfunction LoginDemo()
033         {
034  
035             this._txtUserName = txtUserName;
036             this._txtPassWord = txtPassword;
037             this._txtResult = txtResult;
038             this._btnCall = btnCall;
039             this._btnLogin = btnLogin;
040             this._btnLogOut = btnLogout;
041  
042             this._txtUserName.text ="菩提樹下的楊過";
043             this._txtPassWord.displayAsPassword =true;
044             this._txtPassWord.text ="123456";
045  
046  
047             if(root.loaderInfo.parameters.remotingGatewayPath != null)
048             {
049                 _gatewayUrl = root.loaderInfo.parameters.remotingGatewayPath +"/Gateway.aspx";
050             }
051             else
052             {
053                 _gatewayUrl ="http://localhost:22892/Gateway.aspx";
054  
055             }
056              
057  
058             this._btnLogin.addEventListener(MouseEvent.CLICK,btnLogin_Click);
059             this._btnLogOut.addEventListener(MouseEvent.CLICK,btnLogout_Click);
060             this._btnCall.addEventListener(MouseEvent.CLICK,btnCall_Click);
061  
062  
063         }
064          
065         //登錄
066         privatefunction btnLogin_Click(e:MouseEvent):void
067         {
068             _service =new Service("_04_Authentication.DemoLoginService",_gatewayUrl,ObjectEncoding.AMF3);
069             //這一行是關鍵,用於將用戶名、密碼發送到DemoLoginCommand
070             _service.setRemoteCredentials(this._txtUserName.text,this._txtPassWord.text);                     
071             _rpc = _service.Login();
072             _rpc.addEventListener( ResultEvent.RESULT, loginSuccess );
073             _rpc.addEventListener( FaultEvent.FAULT, loginFailure );
074  
075         }
076  
077         privatefunction loginSuccess( pEvt:ResultEvent ):void
078         {
079             this._txtResult.text ="登錄成功!";
080         }
081  
082  
083  
084         privatefunction loginFailure( pEvt:FaultEvent ):void
085         {
086             this._txtResult.text ="登錄失敗,原因:" + pEvt.fault.description;
087         }
088          
089         //註銷
090         privatefunction btnLogout_Click(e:MouseEvent):void
091         {
092             _service =new Service("_04_Authentication.DemoLoginService",_gatewayUrl,ObjectEncoding.AMF3);
093             _rpc = _service.Logout();
094             _rpc.addEventListener( ResultEvent.RESULT, logoutSuccess );
095             _rpc.addEventListener( FaultEvent.FAULT, logoutFailure );
096  
097         }
098          
099          
100         privatefunction logoutSuccess( pEvt:ResultEvent ):void
101         {
102             this._txtResult.text ="註銷成功!";
103         }
104  
105  
106  
107         privatefunction logoutFailure( pEvt:FaultEvent ):void
108         {
109             this._txtResult.text ="註銷失敗,原因:" + pEvt.fault.description;
110         }
111          
112         //遠程調用需要認證的服務
113         privatefunction btnCall_Click(e:MouseEvent):void
114         {
115             _callService =new Service("_04_Authentication.DemoSecureService",_gatewayUrl,ObjectEncoding.AMF3);
116             _rpc = _callService.HelloWorld();
117             _rpc.addEventListener( ResultEvent.RESULT, callSuccess );
118             _rpc.addEventListener( FaultEvent.FAULT, callFailure );
119  
120         }
121          
122          
123         privatefunction callSuccess( pEvt:ResultEvent ):void
124         {
125             this._txtResult.text ="調用成功:" + pEvt.result;
126         }
127  
128  
129  
130         privatefunction callFailure( pEvt:FaultEvent ):void
131         {
132             this._txtResult.text ="調用失敗,原因:" + pEvt.fault.description;
133         }
134     }
135 }

測試運行的截圖:

這是一上來就直接點擊"遠程調用"的結果,注意右側的大文本框:Requested access is not allowed 即訪問不允許,說明這個服務是需要認證才能調用的。

如果點擊“登錄”後,再點擊"遠程調用",這回成功了,說明認證起作用了。

 

最後再囉嗦一下:前面提到了FluorineFx的認證票據跟asp.net一樣,是保存在Cookie的,所以如果您把swf嵌入到網頁上,在flash中點擊登錄後,如果在其它aspx頁面上用

01 <% if(!User.Identity.IsAuthenticated)
02       {
03           Response.Write("<h5>尚未登錄!</h5>");
04       }
05       else
06       {
07           Response.Write("<h5><span style='color:red'>"+ User.Identity.Name + "</span>已經登錄!</h5>");
08  
09            
10       }
11     %>

同樣也能檢測出用戶的登錄狀態!(前提是不要關閉剛纔那個嵌入swf的頁面)

 

唯一遺憾的是:FluorineFx生成的Cookie認證票據中,並未包含Roles角色信息,所以在AspX頁面上無法用IsInRole來判斷當前用戶的角色(我跟蹤了一下,fluorineFx在Cookie中僅保存了用戶名、密碼以及一些唯一性標識,官方提供的認證演示中雖然有用IsInRole來判斷,但其實是沒用的)。

 

當然這個問題,您可以修改FluorineFx的源碼來解決,這點工作就留給大家了。 

 

不過令人高興的是,反過來卻可以!即:如果在asp.net上登錄了,認證和授權信息在flash裏能識別,通常情況下,這已經能滿足絕大多數需要了。

 

示例源代碼下載: http://cid-2959920b8267aaca.office.live.com/self.aspx/Flash/FluorineFx^_Demo^_04.rar

作者:菩提樹下的楊過
出處:http://yjmyzz.cnblogs.com
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
原文連接:http://www.cnblogs.com/yjmyzz/archive/2010/08/30/1812956.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章