對認證與授權沒啥概念的新同學,建議先看下
.net中的認證(authentication)與授權(authorization),然後再繼續。
Flash/Flex在通過FluorineFx調用.Net中的方法時,同樣也會遇到認證與授權問題,即:
“是否隨便一個阿貓阿狗都能來調用我的方法?”或者可以理解爲:“調用我的方法前是否需要登錄?” 這就是認證
“門衛放進來後,是不是不管什麼身份的人,都能來拿東西?”或者可以理解爲:“登錄後的用戶,具備何種資格的人才能調用方法?” 這就是授權
步驟:
1、先創建自己的LoginCommand類(相當於門衛,用於把關):DemoLoginCommand
01 |
using System.Collections; |
02 |
using System.Security.Principal; |
03 |
using FluorineFx.Security; |
05 |
namespace _04_Authentication |
07 |
public class
DemoLoginCommand:GenericLoginCommand |
09 |
public override
IPrincipal DoAuthentication( string username, Hashtable credentials) |
11 |
string password = credentials[ "password" ] as
string ; |
12 |
if (username.Length > 0) |
14 |
GenericIdentity identity = new
GenericIdentity(username); |
16 |
GenericPrincipal principal = new
GenericPrincipal(identity, new
string [] { "admin" }); |
2、再創建一個不需要認證就能隨便調用的遠程服務 DemoLoginService
02 |
using System.Collections.Generic; |
07 |
namespace _04_Authentication |
10 |
public class
DemoLoginService |
22 |
new DemoLoginCommand().Logout( null ); |
3、爲了對比,再來創建一個需要認證的遠程服務:DemoSecureService
02 |
using System.Collections.Generic; |
07 |
namespace _04_Authentication |
10 |
public class
DemoSecureService |
13 |
public string
HelloWorld() |
等一下,提個問題:比較這個服務跟剛纔創建的服務,除了裏面的方法名稱(及相關的方法處理邏輯)不同,本質上沒區別吧? 憑啥說調用這個服務就需要認證,而剛纔那個服務就能隨便調用?
很好,希望大家帶着這個問題繼續,後面會揭曉。
先打個岔:回想一下asp.net中後臺目錄權限的處理,我們可以在web.config 中通過配置來決定某個目錄是否可訪問
這段配置的意思就是 /admin目錄,匿名用戶無法訪問(即要求登錄),同時"買家","賣家"二種角色被拒絕了(即:就算你登錄了,只要你是"買家"或"賣家"角色,同樣也訪問不了)
FluorineFx中,同樣也是用配置來實現權限訪問的:
先看remoting-config.xml的配置
01 |
<? xml version = "1.0" encoding = "UTF-8" ?> |
02 |
< service id = "remoting-service" |
03 |
class = "flex.messaging.services.RemotingService" |
04 |
messageTypes = "flex.messaging.messages.RemotingMessage" > |
08 |
< adapter-definition id = "dotnet" class = "FluorineFx.Remoting.RemotingAdapter" default = "true" /> |
12 |
< channel ref = "my-amf" /> |
17 |
< destination id = "fluorine" > |
24 |
< security-constraint ref = "privileged-users" /> |
29 |
< destination id = "login" > |
31 |
< source >_04_Authentication.DemoLoginService</ source > |
關鍵地方已經註釋了,這個配置就表明了_04_Authentication.DemoLoginService不需要登錄就能調用,而其它服務調用時要受到"privileged-users"的限制,那麼這個限制到底如何描述的呢?
services-config.xml配置
01 |
<? xml version = "1.0" encoding = "utf-8" ?> |
04 |
< service-include file-path = "remoting-config.xml" /> |
10 |
< security-constraint id = "privileged-users" > |
12 |
< auth-method >Custom</ auth-method > |
18 |
</ security-constraint > |
20 |
< login-command class = "_04_Authentication.DemoLoginCommand" server = "asp.net" /> |
24 |
< channel-definition id = "my-amf" class = "mx.messaging.channels.AMFChannel" > |
25 |
< endpoint uri = "http://{server.name}:{server.port}/{context.root}/Gateway.aspx" class = "flex.messaging.endpoints.AMFEndpoint" /> |
同樣:重點地方已經加了註釋。
另外一個重要配置:fluorineFx說到底是宿主在asp.net iis環境中的,所以它的認證票據同樣是保存在cookie中的,web.config的表單認證方式要設置爲Forms,即
04 |
< compilation debug = "true" targetFramework = "4.0" /> |
07 |
< add name = "FluorineGateway" type = "FluorineFx.FluorineGateway,
FluorineFx" /> |
11 |
< authentication mode = "Forms" ></ authentication > |
ok,服務端就全部完成了,再來看Flash端的調用:
UI界面:
先講下我們要做什麼:
a、點擊“登錄”或“註銷”時,調用不需要登錄的DemoLoginService
b、點擊"遠程調用"時,調用需要認證的DemoSecureService
預測一下結果:
點擊“登錄”前,如果直接點擊“遠程調用”,應該會調用失敗(因此此時尚未登錄認證)
如果先點擊“登錄”後,再點擊“遠程調用”,因爲這時已經登錄認證過了,所以應該成功
完整flash代碼:
003 |
import flash.display.Sprite; |
004 |
import flash.net.ObjectEncoding; |
005 |
import org.bytearray.remoting.Service; |
006 |
import org.bytearray.remoting.PendingCall; |
007 |
import org.bytearray.remoting.events.ResultEvent; |
008 |
import org.bytearray.remoting.events.FaultEvent; |
009 |
import fl.controls.TextInput; |
010 |
import flash.text.TextField; |
011 |
import fl.controls.TextArea; |
012 |
import fl.controls.Button; |
013 |
import flash.events.MouseEvent; |
017 |
public class
LoginDemo extends Sprite |
019 |
private var
_txtUserName:TextInput; |
020 |
private var
_txtPassWord:TextInput; |
021 |
private var
_txtResult:TextArea; |
022 |
private var
_btnLogin:Button; |
023 |
private var
_btnLogOut:Button; |
024 |
private var
_btnCall:Button; |
026 |
private var
_gatewayUrl: String =
"" ; |
027 |
private var
_service:Service; |
028 |
private var
_callService:Service; |
029 |
private var
_rpc:PendingCall; |
032 |
public function
LoginDemo() |
035 |
this ._txtUserName = txtUserName; |
036 |
this ._txtPassWord = txtPassword; |
037 |
this ._txtResult = txtResult; |
038 |
this ._btnCall = btnCall; |
039 |
this ._btnLogin = btnLogin; |
040 |
this ._btnLogOut = btnLogout; |
042 |
this ._txtUserName.text = "菩提樹下的楊過" ; |
043 |
this ._txtPassWord.displayAsPassword = true ; |
044 |
this ._txtPassWord.text = "123456" ; |
047 |
if (root.loaderInfo.parameters.remotingGatewayPath !=
null ) |
049 |
_gatewayUrl = root.loaderInfo.parameters.remotingGatewayPath + "/Gateway.aspx" ; |
058 |
this ._btnLogin.addEventListener(MouseEvent.CLICK,btnLogin_Click); |
059 |
this ._btnLogOut.addEventListener(MouseEvent.CLICK,btnLogout_Click); |
060 |
this ._btnCall.addEventListener(MouseEvent.CLICK,btnCall_Click); |
066 |
private function
btnLogin_Click(e:MouseEvent): void |
068 |
_service = new
Service( "_04_Authentication.DemoLoginService" ,_gatewayUrl,ObjectEncoding.AMF3); |
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 ); |
077 |
private function
loginSuccess( pEvt:ResultEvent ): void |
079 |
this ._txtResult.text = "登錄成功!" ; |
084 |
private function
loginFailure( pEvt:FaultEvent ): void |
086 |
this ._txtResult.text = "登錄失敗,原因:"
+ pEvt.fault.description; |
090 |
private function
btnLogout_Click(e:MouseEvent): void |
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 ); |
100 |
private function
logoutSuccess( pEvt:ResultEvent ): void |
102 |
this ._txtResult.text = "註銷成功!" ; |
107 |
private function
logoutFailure( pEvt:FaultEvent ): void |
109 |
this ._txtResult.text = "註銷失敗,原因:"
+ pEvt.fault.description; |
113 |
private function
btnCall_Click(e:MouseEvent): void |
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 ); |
123 |
private function
callSuccess( pEvt:ResultEvent ): void |
125 |
this ._txtResult.text = "調用成功:"
+ pEvt.result; |
130 |
private function
callFailure( pEvt:FaultEvent ): void |
132 |
this ._txtResult.text = "調用失敗,原因:"
+ pEvt.fault.description; |
測試運行的截圖:
這是一上來就直接點擊"遠程調用"的結果,注意右側的大文本框:Requested access is not allowed 即訪問不允許,說明這個服務是需要認證才能調用的。
如果點擊“登錄”後,再點擊"遠程調用",這回成功了,說明認證起作用了。
最後再囉嗦一下:前面提到了FluorineFx的認證票據跟asp.net一樣,是保存在Cookie的,所以如果您把swf嵌入到網頁上,在flash中點擊登錄後,如果在其它aspx頁面上用
01 |
<% if (!User.Identity.IsAuthenticated) |
03 |
Response.Write( "<h5>尚未登錄!</h5>" ); |
07 |
Response.Write( "<h5><span style='color:red'>" + User.Identity.Name +
"</span>已經登錄!</h5>" ); |
同樣也能檢測出用戶的登錄狀態!(前提是不要關閉剛纔那個嵌入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