zoukankan      html  css  js  c++  java
  • FluorineFx:认证与授权

    对认证与授权没啥概念的新同学,建议先看下 .net中的认证(authentication)与授权(authorization),然后再继续。

    Flash/Flex在通过FluorineFx调用.Net中的方法时,同样也会遇到认证与授权问题,即:

     “是否随便一个阿猫阿狗都能来调用我的方法?”或者可以理解为:“调用我的方法前是否需要登录?” 这就是认证

    “门卫放进来后,是不是不管什么身份的人,都能来拿东西?”或者可以理解为:“登录后的用户,具备何种资格的人才能调用方法?” 这就是授权

    步骤:

    1、先创建自己的LoginCommand类(相当于门卫,用于把关):DemoLoginCommand

    using System.Collections;
    using System.Security.Principal;
    using FluorineFx.Security;
    
    namespace _04_Authentication
    {
        public class DemoLoginCommand:GenericLoginCommand
        {
            public override IPrincipal DoAuthentication(string username, Hashtable credentials)
            {
                string password = credentials["password"] as string;           
                if (username.Length > 0)//当然:这里只是演示,实际应用中可从数据库中检验用户名和密码
                {
                    GenericIdentity identity = new GenericIdentity(username);
                    //下面的代码,授予登录用户:"admin"角色,仍然只是演示,实际应用中,可以根据用户名到数据库里查询对应的角色
                    GenericPrincipal principal = new GenericPrincipal(identity, new string[] { "admin"});
    
                    return principal;
                }
                else //检验不通过的处理
                {
                    return null;
                }
            }
        }
    }
    

    2、再创建一个不需要认证就能随便调用的远程服务 DemoLoginService

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using FluorineFx;
    
    namespace _04_Authentication
    {
        [RemotingService]
        public class DemoLoginService
        {
            //其实这个方法随便起什么名字都行,只要在flash中调用时跟这里一致就行(可以理解为方法占位符),但是通常为了有意义,命名为Login
            public bool Login()
            {
                //这个返回值,其实返回true或false都无所谓,重点不在这里
                return true;
            }
    
            //同样,这里方法的名字其实也可以随便起
            public bool Logout()
            {
                new DemoLoginCommand().Logout(null); //这一行才是关键,相当于把"认证信息"清空了
                return true;//明白了上一行后,其实也应该能想到:这里返回true或flase其实都不重要
            }
        }
    }
    

    3、为了对比,再来创建一个需要认证的远程服务:DemoSecureService

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using FluorineFx;
    
    namespace _04_Authentication
    {
        [RemotingService]
        public class DemoSecureService
        {         
            //太简单了,不作解释了
            public string HelloWorld()
            {
                return "从服务端返回的安全数据.";
            }
        }
    }
    

    等一下,提个问题:比较这个服务跟刚才创建的服务,除了里面的方法名称(及相关的方法处理逻辑)不同,本质上没区别吧? 凭啥说调用这个服务就需要认证,而刚才那个服务就能随便调用?

    很好,希望大家带着这个问题继续,后面会揭晓。

    先打个岔:回想一下asp.net中后台目录权限的处理,我们可以在web.config 中通过配置来决定某个目录是否可访问

    <location path="admin">
    	<system.web>
    		<authorization>
    			<deny users="?" />
    			<deny roles="买家" />
    			<deny roles="卖家" />
    		</authorization>
    	</system.web>
    </location>
    

    这段配置的意思就是 /admin目录,匿名用户无法访问(即要求登录),同时"买家","卖家"二种角色被拒绝了(即:就算你登录了,只要你是"买家"或"卖家"角色,同样也访问不了)

    FluorineFx中,同样也是用配置来实现权限访问的:

    先看remoting-config.xml的配置

    <?xml version="1.0" encoding="UTF-8"?>
    <service id="remoting-service"
        class="flex.messaging.services.RemotingService"
        messageTypes="flex.messaging.messages.RemotingMessage">
    
    
      <adapters>
        <adapter-definition id="dotnet" class="FluorineFx.Remoting.RemotingAdapter" default="true"/>
      </adapters>
    
      <default-channels>
        <channel ref="my-amf"/>
      </default-channels>
    
    
    
      <destination id="fluorine">
        <properties>
          <!--这里表示所有源(文件)包括在内-->
          <source>*</source>
        </properties>
        <security>
          <!--这里表明访问上面source中定义的服务时,必须满足"privileged-users"的安全限制-->
          <security-constraint ref="privileged-users"/>
        </security>
      </destination>
    
    
      <destination id="login">
        <properties>
          <source>_04_Authentication.DemoLoginService</source>
          <!--注意这里没有<security />节点,即_04_Authentication.DemoLoginService的访问没有安全限制,也就是可随便调用-->
        </properties>
      </destination>
    
    </service>
    
    

    关键地方已经注释了,这个配置就表明了_04_Authentication.DemoLoginService不需要登录就能调用,而其它服务调用时要受到"privileged-users"的限制,那么这个限制到底如何描述的呢?

    services-config.xml配置

    <?xml version="1.0" encoding="utf-8" ?>
    <services-config>
      <services>
        <service-include file-path="remoting-config.xml" />
      </services>
    
    
      <security>
        <!--这里跟前面的"privileged-users"对应上了-->
        <security-constraint id="privileged-users">
          <!--表明认证方法为开发人员自定义-->
          <auth-method>Custom</auth-method>
          <!--调用该限制相关的服务,必须是admin或user角色-->
          <roles>
            <role>admin</role>
            <role>user</role>
          </roles>
        </security-constraint>
        <!--这里指明了验证登录时,由谁来把关,即DemoLoginCommand-->
        <login-command class="_04_Authentication.DemoLoginCommand" server="asp.net"/>
      </security>
    
      <channels>
        <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
          <endpoint uri="http://{server.name}:{server.port}/{context.root}/Gateway.aspx" class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
        <properties>
          <!-- <legacy-collection>true</legacy-collection> -->
        </properties>
      </channels>
    </services-config>
    

    同样:重点地方已经加了注释。

    另外一个重要配置:fluorineFx说到底是宿主在asp.net iis环境中的,所以它的认证票据同样是保存在cookie中的,web.config的表单认证方式要设置为Forms,即

    <?xml version="1.0"?>
    <configuration>
      <system.web>
        <compilation debug="true" targetFramework="4.0" />
    
        <httpModules>
          <add name="FluorineGateway" type="FluorineFx.FluorineGateway, FluorineFx"/>
        </httpModules>
    
         <!--这里要指定为Forms认证方式-->
        <authentication mode="Forms"></authentication>
        
      </system.web>
    </configuration>
    

    ok,服务端就全部完成了,再来看Flash端的调用:

    UI界面:

     

    先讲下我们要做什么:

    a、点击“登录”或“注销”时,调用不需要登录的DemoLoginService

    b、点击"远程调用"时,调用需要认证的DemoSecureService

    预测一下结果:

    点击“登录”前,如果直接点击“远程调用”,应该会调用失败(因此此时尚未登录认证)

    如果先点击“登录”后,再点击“远程调用”,因为这时已经登录认证过了,所以应该成功

    完整flash代码:

    package 
    {
    	import flash.display.Sprite;
    	import flash.net.ObjectEncoding;
    	import org.bytearray.remoting.Service;
    	import org.bytearray.remoting.PendingCall;
    	import org.bytearray.remoting.events.ResultEvent;
    	import org.bytearray.remoting.events.FaultEvent;
    	import fl.controls.TextInput;
    	import flash.text.TextField;
    	import fl.controls.TextArea;
    	import fl.controls.Button;
    	import flash.events.MouseEvent;
    
    
    
    	public class LoginDemo extends Sprite
    	{
    		private var _txtUserName:TextInput;
    		private var _txtPassWord:TextInput;
    		private var _txtResult:TextArea;
    		private var _btnLogin:Button;
    		private var _btnLogOut:Button;
    		private var _btnCall:Button;
    
    		private var _gatewayUrl:String = "";
    		private var _service:Service;		
    		private var _callService:Service;
    		private var _rpc:PendingCall;
    		
    
    		public function LoginDemo()
    		{
    
    			this._txtUserName = txtUserName;
    			this._txtPassWord = txtPassword;
    			this._txtResult = txtResult;
    			this._btnCall = btnCall;
    			this._btnLogin = btnLogin;
    			this._btnLogOut = btnLogout;
    
    			this._txtUserName.text = "菩提树下的杨过";
    			this._txtPassWord.displayAsPassword = true;
    			this._txtPassWord.text = "123456";
    
    
    			if (root.loaderInfo.parameters.remotingGatewayPath != null)
    			{
    				_gatewayUrl = root.loaderInfo.parameters.remotingGatewayPath + "/Gateway.aspx";
    			}
    			else
    			{
    				_gatewayUrl = "http://localhost:22892/Gateway.aspx";
    
    			}
    			
    
    			this._btnLogin.addEventListener(MouseEvent.CLICK,btnLogin_Click);
    			this._btnLogOut.addEventListener(MouseEvent.CLICK,btnLogout_Click);
    			this._btnCall.addEventListener(MouseEvent.CLICK,btnCall_Click);
    
    
    		}
    		
    		//登录
    		private function btnLogin_Click(e:MouseEvent):void
    		{
    			_service = new Service("_04_Authentication.DemoLoginService",_gatewayUrl,ObjectEncoding.AMF3);
    			//这一行是关键,用于将用户名、密码发送到DemoLoginCommand
    			_service.setRemoteCredentials(this._txtUserName.text, this._txtPassWord.text);						
    			_rpc = _service.Login();
    			_rpc.addEventListener( ResultEvent.RESULT, loginSuccess );
    			_rpc.addEventListener( FaultEvent.FAULT, loginFailure );
    
    		}
    
    		private function loginSuccess( pEvt:ResultEvent ):void
    		{
    			this._txtResult.text = "登录成功!";
    		}
    
    
    
    		private function loginFailure( pEvt:FaultEvent ):void
    		{
    			this._txtResult.text = "登录失败,原因:" + pEvt.fault.description;
    		}
    		
    		//注销
    		private function btnLogout_Click(e:MouseEvent):void
    		{
    			_service = new Service("_04_Authentication.DemoLoginService",_gatewayUrl,ObjectEncoding.AMF3);
    			_rpc = _service.Logout();
    			_rpc.addEventListener( ResultEvent.RESULT, logoutSuccess );
    			_rpc.addEventListener( FaultEvent.FAULT, logoutFailure );
    
    		}
    		
    		
    		private function logoutSuccess( pEvt:ResultEvent ):void
    		{
    			this._txtResult.text = "注销成功!";
    		}
    
    
    
    		private function logoutFailure( pEvt:FaultEvent ):void
    		{
    			this._txtResult.text = "注销失败,原因:" + pEvt.fault.description;
    		}
    		
    		//远程调用需要认证的服务
    		private function btnCall_Click(e:MouseEvent):void
    		{
    			_callService = new Service("_04_Authentication.DemoSecureService",_gatewayUrl,ObjectEncoding.AMF3);
    			_rpc = _callService.HelloWorld();
    			_rpc.addEventListener( ResultEvent.RESULT, callSuccess );
    			_rpc.addEventListener( FaultEvent.FAULT, callFailure );
    
    		}
    		
    		
    		private function callSuccess( pEvt:ResultEvent ):void
    		{
    			this._txtResult.text = "调用成功:" + pEvt.result;
    		}
    
    
    
    		private function callFailure( pEvt:FaultEvent ):void
    		{
    			this._txtResult.text = "调用失败,原因:" + pEvt.fault.description;
    		}
    	}
    }
    

    测试运行的截图:

    这是一上来就直接点击"远程调用"的结果,注意右侧的大文本框:Requested access is not allowed 即访问不允许,说明这个服务是需要认证才能调用的。

    如果点击“登录”后,再点击"远程调用",这回成功了,说明认证起作用了。

    最后再啰嗦一下:前面提到了FluorineFx的认证票据跟asp.net一样,是保存在Cookie的,所以如果您把swf嵌入到网页上,在flash中点击登录后,如果在其它aspx页面上用

     <% if (!User.Identity.IsAuthenticated)
           {
               Response.Write("<h5>尚未登录!</h5>");
           }
           else 
           {
               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

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    《JavaScript语言精粹》小记
    JavaScript之单例实战
    浅谈requireJS
    细说gulp
    Javascript之自定义事件
    ClipboardJS复制粘贴插件的使用
    重新学习vue基础
    正则简单说明
    JavaScript字符串api简单说明
    移动端浏览器问题
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1812956.html
Copyright © 2011-2022 走看看