<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-193  評論-715  文章-1  trackbacks-0
    在昨天的文章《BlazeDS結合Tomcat進行權限控制》中,講述了BlazeDS如何在Tomcat環境下進行權限控制,但是我們不難發現有很多缺點,甚至有一些都是致命的,比如不能跨平臺(中間件),甚至不能跨版本,還有用戶名角色等配置不能自定義配置在RDBMS,文件或其它地方等。所以今天我要分享給大家如何擺脫這些限制,避免這些不利因素。

    所幸的是,BlazeDS的設計者們已經為我們想到了這些,我們只需要采用自定義認證的方式即可,具體實現時,需要實現flex.messaging.security.LoginCommand這個接口。我們不妨先來看看這個接口的定義(直接上代碼了):
    package flex.messaging.security;

    import javax.servlet.ServletConfig;

    import java.security.Principal;
    import java.util.List;

    /**
     * The class name of the implementation of this interface is configured in the
     * gateway configuration's security section and is instantiated using reflection
     * on servlet initialization.
     
    */

    public interface LoginCommand
    {
        
    /**
         * Called to initialize a login command prior to authentication/authorization requests.
         * 
         * 
    @param config The servlet configuration for MessageBrokerServlet.  
         
    */

        
    void start(ServletConfig config);

        
    /**
         * Called to free up resources used by the login command.
         
    */

        
    void stop();

        
    /**
         * The gateway calls this method to perform programmatic, custom authentication.
         * <p>
         * The credentials are passed as a Map to allow for extra properties to be
         * passed in the future. For now, only a "password" property is sent.
         * </p>
         *
         * 
    @param username    The principal being authenticated
         * 
    @param credentials A map, typically with string keys and values - holds, for example, a password
         * 
    @return principal for the authenticated user when authentication is successful; null otherwise 
         
    */

        Principal doAuthentication(String username, Object credentials);

        
    /**
         * The gateway calls this method to perform programmatic authorization.
         * <p>
         * A typical implementation would simply iterate over the supplied roles and
         * check that at least one of the roles returned true from a call to
         * HttpServletRequest.isUserInRole(String role).
         * </p>
         *
         * 
    @param principal The principal being checked for authorization
         * 
    @param roles    A List of role names to check, all members should be strings
         * 
    @return true if the principal is authorized given the list of roles
         
    */

        
    boolean doAuthorization(Principal principal, List roles);

        
    /**
         * Attempts to log a user out from their session.
         *
         * NOTE: May not be possible on all application servers.
         * 
    @param principal The principal to logout.
         * 
    @return true when logout is successful
         
    */

        
    boolean logout(Principal principal);
    }

    最主要的3個方法:doAuthentication()用來認證,doAuthorization()用來進行授權,logout()用來執行登出時的動作,主要是釋放Principal,關于Principal的概念,直接來自于Java,如需進一步了解,也可以參考JAAS的相關知識,我在之前的學習筆記《JAAS Study Note 》中也簡單的提及過,在此就不再多講了,廢話不多說了,直接上步驟了,這應該是大家喜歡的方式:

    1,實現一個自定義的Principal:
    package com.robin.common.security;

    import java.security.Principal;
    import java.util.List;

    public class UserPrincipal implements Principal, java.io.Serializable {

        
    private String name;
        
    private List<String> subjects;

        
    /**
         * Create a SamplePrincipal with a Sample username.
         * 
         * <p>
         * 
         * 
    @param name
         *            the Sample username for this user.
         * 
         * 
    @exception NullPointerException
         *                if the <code>name</code> is <code>null</code>.
         
    */

        
    public UserPrincipal(String name) {
            
    if (name == null)
                
    throw new NullPointerException("illegal null input");

            
    this.name = name;
        }


        
    public List<String> getSubjects() {
            
    return subjects;
        }


        
    public void setSubjects(List<String> subjects) {
            
    this.subjects = subjects;
        }


        
    public String getName() {
            
    return name;
        }


        
    /**
         * Return a string representation of this <code>SamplePrincipal</code>.
         * 
         * <p>
         * 
         * 
    @return a string representation of this <code>SamplePrincipal</code>.
         
    */

        
    public String toString() {
            
    return ("Principal's username:  " + name);
        }


        
    /**
         * Compares the specified Object with this <code>SamplePrincipal</code> for
         * equality. Returns true if the given object is also a
         * <code>SamplePrincipal</code> and the two SamplePrincipals have the same
         * username.
         * 
         * <p>
         * 
         * 
    @param o
         *            Object to be compared for equality with this
         *            <code>SamplePrincipal</code>.
         * 
         * 
    @return true if the specified Object is equal equal to this
         *         <code>SamplePrincipal</code>.
         
    */

        
    public boolean equals(Object o) {
            
    if (o == null)
                
    return false;

            
    if (this == o)
                
    return true;

            
    if (!(o instanceof UserPrincipal))
                
    return false;
            UserPrincipal that 
    = (UserPrincipal) o;

            
    if (this.getName().equals(that.getName()))
                
    return true;
            
    return false;
        }


        
    /**
         * Return a hash code for this <code>SamplePrincipal</code>.
         * 
         * <p>
         * 
         * 
    @return a hash code for this <code>SamplePrincipal</code>.
         
    */

        
    public int hashCode() {
            
    return name.hashCode();
        }

    }


    2,實現自定義的LoginCommand:
    package com.robin.common.security;

    import java.security.Principal;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;

    import javax.servlet.ServletConfig;

    import flex.messaging.io.MessageIOConstants;
    import flex.messaging.security.LoginCommand;

    public class RdbmsLoginCommand implements LoginCommand {

        
    public Principal doAuthentication(String username, Object credentials) {
            String password 
    = extractPassword(credentials);
            System.out.println(
    "###Username:"+username+",Password:"+password+"###");
            
    //TODO: use this user name and password to validate from RDBMS.
            
    //And then, query user's roles and set to principle.
            if(true){
                UserPrincipal principal 
    = new UserPrincipal(username);
                List
    <String> subjects = new ArrayList<String>();
                subjects.add(
    "ROLE_AD");
                
    if(username.equals("admin")){
                    subjects.add(
    "ADMIN");
                }

                principal.setSubjects(subjects);
                
    return principal;
            }
     else{
                
    return null;
            }

        }


        
    public boolean doAuthorization(Principal principal, List roles) {
            System.out.println(principal
    +"##########################");
            UserPrincipal p
    =(UserPrincipal)principal;
            List
    <String> subjects = p.getSubjects();
            
    for (int i = 0; i < subjects.size(); i++{
                String subject
    = subjects.get(i);
                
    for (int j = 0; j < roles.size(); j++{
                    System.out.print(roles.get(j)
    +"$$$");
                    
    if(subject.equals(roles.get(j))){
                        
    return true;
                    }

                }

            }

            
    return false;
        }


        
    public boolean logout(Principal principal) {
            System.out.println(principal
    +"will logout at once.");
            principal 
    = null;
            
    return true;
        }


        
    public void start(ServletConfig arg0) {
            
        }


        
    public void stop() {

        }

        
        
    private String extractPassword(Object credentials)
        
    {
            String password 
    = null;
            
    if (credentials instanceof String)
            
    {
                password 
    = (String)credentials;
            }

            
    else if (credentials instanceof Map)
            
    {
                password 
    = (String)((Map)credentials).get(MessageIOConstants.SECURITY_CREDENTIALS);
            }

            
    return password;
        }


    }

    這些代碼都非常簡單,我想就不用我再解釋了。

    3,在BlazeDS中配置security-constraint,先配置service-config.xml:
    <security>
            
    <login-command class="com.robin.common.security.RdbmsLoginCommand" server="all"/>
            
    <security-constraint id="administrators">
            
    <auth-method>Custom</auth-method>
                
    <roles>
                    
    <role>ADMIN</role>
                
    </roles>
            
    </security-constraint>
            
    <security-constraint id="users">
                
    <auth-method>Custom</auth-method>
                
    <roles>
                    
    <role>ROLE_AD</role>
                    
    <role>ADMIN</role>
                
    </roles>
            
    </security-constraint>
        
    </security>

    然后在remote-config.xml中配置每個destination的授權規則:
    <destination id="DomainService">
            
    <properties>
                
    <source>com.robin.service.domain.DomainService</source>
                
    <include-methods>
                
    <method name="getAllDomains"/>
                
    <method name="addOrUpdateDomain" security-constraint="administrators"/>
                
    </include-methods>
            
    </properties>
            
    <security>
                
    <security-constraint ref="users"/>
            
    </security>
        
    </destination>

    4,服務端的配置就大功告成了,現在來看看客戶端如何實現登錄:
    <?xml version = "1.0" encoding = "utf-8"?>
    <mx:Module xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" xmlns:mx = "library://ns.adobe.com/flex/mx" layout = "absolute" width = "100%" height = "100%"
               xmlns:component 
    = "com.robin.common.component.*">
        
        
    <fx:Declarations>
            
    <mx:RemoteObject id = "loginService" destination = "remoting_AMF_SecurityConstraint_Custom" showBusyCursor = "true" fault = "Alert.show(event.fault.faultString, 'Error');"/>
        
    </fx:Declarations>
        
    <fx:Script>
            
    <![CDATA[
                import com.robin.common.events.SwitchModuleEvent;
                import mx.controls.Alert;
                import mx.messaging.ChannelSet;
                import mx.messaging.config.ServerConfig;
                import mx.rpc.AsyncResponder;
                import mx.rpc.AsyncToken;
                import mx.rpc.events.FaultEvent;
                import mx.rpc.events.ResultEvent;

                // Define a ChannelSet object.
                public var cs:ChannelSet;
                // Define an AsyncToken object.
                public var token:AsyncToken;

                // Initialize ChannelSet object based on the
               // destination of the RemoteObject component.
                private function creationCompleteHandler():void {
                    if (cs == null)
                       cs = ServerConfig.getChannelSet(loginService.destination);
                }

                // Login and handle authentication success or failure.
                private function login():void {
                    // Make sure that the user is not already logged in.
                    var user:String = username.text;
                    var pwd:String = password.text;
                    if (user == "" || pwd == "") {
                        Alert.show("User name or password is empty, please check them.", "Info");
                        return;
                    }
                    if (this.parentApplication.cs.authenticated == false) {
                        this.parentApplication.token = this.parentApplication.cs.login(user, pwd);
                        // Add result and fault handlers.
                        this.parentApplication.token.addResponder(new AsyncResponder(LoginResultEvent, LoginFaultEvent));
                    }
                }

                // Handle successful login.
                private function LoginResultEvent(event:ResultEvent, token:Object = null):void {
                    switch (event.result) {
                        case "success":
                            break;
                        default:
                    }
                    var switchEvent:SwitchModuleEvent = new SwitchModuleEvent(SwitchModuleEvent.SWITCH, "");
                    dispatchEvent(switchEvent);
                }

                // Handle login failure.
                private function LoginFaultEvent(event:FaultEvent, token:Object = null):void {
                    trace(event.fault.faultCode);
                    switch (event.fault.faultCode) {
                        case "Client.Authentication":
                        default:
                            Alert.show("Login failure: " + event.fault.faultString);
                    }
                }

            
    ]]>
        
    </fx:Script>
        
    <mx:HBox x="100" y = "100">
            
    <component:RequiredLabel text = "user name:" isRequired = "true"/>
            
    <s:TextInput id = "username" text = "admin"/>
            
    <component:RequiredLabel text = "password:" isRequired = "true"/>
            
    <s:TextInput id = "password" text = "" displayAsPassword = "true"/>
            
    <s:Button label = "Login" click = "login();"/>
        
    </mx:HBox>
        
    <s:Label x="100" y="130" text="Notes: You can use any user to register, and if you wanna access add or update functions, you need to user 'admin' user. "/>

    </mx:Module>

    主要是用ChannelSet.login()方法和logout()方法進行登錄與登出,登出的詳細代碼在此就省略了,有興趣的朋友可以自己試試或者參考Adobe官方的《BlazeDS dev guide》。

    小結一下:
    1,解決了與Tomcat等中間件綁定的問題。
    2,可以將用戶和角色的對應關系存放在RDBMS中,并可以開發相應功能進行動態編輯而不需要重啟中間件。
    3,可以自定義登錄界面,而不是借助于瀏覽器的窗口和HTTP BASIC方式。
    4,不好之處是,由于角色已經在代碼或配置中綁定,無法動態新增角色。


    大家如有不清楚與需要討論的地方,歡迎留言!

    本Blog所有內容不得隨意轉載,版權屬于作者所有。如需轉載請與作者聯系( fastzch@163.com    QQ:9184314)。
    未經許可的轉載,本人保留一切法律權益。
    一直以來,發現有某些人完全不尊重我的勞動成果,隨意轉載,提醒一下那些人小心哪天惹上官司。



    posted on 2010-04-28 09:56 Robin's Programming World 閱讀(3031) 評論(3)  編輯  收藏 所屬分類: JavaFlex & Flash

    評論:
    # re: BlazeDS自定義認證與權限控制 2010-04-28 11:25 | 俏物悄語
    看見就撒嬌的撒  回復  更多評論
      
    # re: BlazeDS自定義認證與權限控制[未登錄] 2014-03-11 13:16 | yxy
    <mx:RemoteObject id = "loginService" destination = "remoting_AMF_SecurityConstraint_Custom"這里的destination在哪里定義?是干什么的  回復  更多評論
      
    # re: BlazeDS自定義認證與權限控制 2015-06-14 14:08 | 內誰
    NB!!!  回復  更多評論
      
    主站蜘蛛池模板: 亚洲色成人网站WWW永久| 亚洲成人高清在线观看| 成人无码WWW免费视频| 亚洲三级电影网站| 爽爽日本在线视频免费| 一级毛片免费在线| 亚洲国产视频网站| 亚洲片国产一区一级在线观看| 99免费在线观看视频| 亚洲精品无码久久久久久| 亚洲午夜久久久久妓女影院 | 免费大片av手机看片高清| 亚洲狠狠婷婷综合久久蜜芽| 亚洲Av无码乱码在线znlu| 真实国产乱子伦精品免费| 亚洲国产高清美女在线观看| 国产AⅤ无码专区亚洲AV| 国产男女猛烈无遮挡免费视频网站 | 在线91精品亚洲网站精品成人| 亚洲六月丁香六月婷婷色伊人| 久久精品国产亚洲AV果冻传媒| 亚洲综合国产精品第一页| 亚洲AV无码乱码精品国产| 国产福利免费观看| 国产美女无遮挡免费视频网站| 毛片大全免费观看| 久久久久免费看黄A片APP| 国产黄色免费网站| 美女视频黄是免费的网址| 久久九九兔免费精品6| 国产精品久久永久免费| 国产大片免费网站不卡美女| 亚洲免费在线视频| 亚洲色四在线视频观看| 亚洲中文字幕无码一久久区| 亚洲一级黄色视频| 亚洲综合国产精品第一页| 久久久久国产成人精品亚洲午夜| 国产成人精品日本亚洲专区| 在线观看亚洲天天一三视| 国产精品亚洲A∨天堂不卡|