对认证与授权没啥概念的新同学,建议先看下 .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