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

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

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

    Jungleford's Home BlogJava分舵

    Java技術(shù)研究,兼探討歷史話題

    BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
      24 Posts :: 0 Stories :: 53 Comments :: 0 Trackbacks

    jungleford如是說

         安全性是
    Java鼓吹得最多的特性之一,的確,Java的安全特性涵蓋了從應(yīng)用級別到語言級別乃至JVM本身。以前大家都知道有個Sandbox,但僅有Sandbox尚不能滿足,或者說不能很方便地做到我們所需要的全部安全需求,譬如現(xiàn)在一個系統(tǒng)首先起碼需要一個登錄功能,更進一步的話,還需要對用戶訪問資源的行為進行約束,下面我想大致講一下Java是怎樣做這些事情的,基本上是一個總結(jié)或者說是“讀后感”的性質(zhì),同時給出一個簡單的實現(xiàn)例子,這個例子其實還是模仿人家的,呵呵……

    1.Java的訪問控制機制

         談到訪問控制,或者說“授權(quán)”,這里有兩層含義,一是從資源的角度,這個socket端口是否被允許操作?這個文件是可讀的?可寫的?還是可執(zhí)行的?還是以上都行?這就是我們在UNIX下用“l(fā)s -l”命令列出當(dāng)前目錄下文件時,那些“-rwx-”之類的含義;二是從訪問者的角度,我想通過80端口看Web上新浪歐洲杯的新聞,在這個系統(tǒng)中有沒有這個資格?我想播放D盤上一個名為“friends.rm”的視頻文件,我得到了訪問這個文件的權(quán)限了嗎?我有運行播放器的權(quán)限嗎?
         Java在訪問控制策略上同時考慮了這兩方面內(nèi)容,你說“不對呀,我用
    FileOutputStream寫文件,用Socket類連接遠程主機都用得好好的,沒什么限制呀”,這我們得先談?wù)勈裁唇凶觥?B>安全管理器”(SecurityManger)。安全管理器從JDK 1.0就開始有了,多古老啊!Java從設(shè)計的那一天開始就考慮了安全因素,安全管理器是Sandbox的最重要的一個部分,也是訪問控制的總協(xié)調(diào)者,我們能夠在通常情況下正常使用網(wǎng)絡(luò)和文件,那是因為當(dāng)啟動application的時候(注意是application,不是applet!),如果你不加“-Djava.security.manager”選項,JVM是不會啟動Sandbox的,這時你可以“為所欲為”,而不會碰到SecurityException之類的異常;一旦加入了“-Djava.security.manager”選項,你就會發(fā)現(xiàn)有一連串的異常出現(xiàn)嘍!

    Exception in thread "main" java.security.AccessControlException: access denied (……)
    ……


    Java內(nèi)置了一個默認的安全策略,這種情況下安全管理器首先裝載的是這個默認的策略,不信啊,不信你檢查一下你的“%JAVA_HOME%\jre\lib\security\”目錄,是不是有個叫“java.policy”的文件?用notepad打開看看:


    // Standard extensions get all permissions by default

    grant codeBase "file:${java.home}/lib/ext/*" {
        permission java.security.AllPermission;
    };

    // default permissions granted to all domains


    grant { 
        // Allows any thread to stop itself using the java.lang.Thread.stop()

        // method that takes no argument.
        // Note that this permission is granted by default only to remain
        // backwards compatible.
        // It is strongly recommended that you either remove this permission
        // from this policy file or further restrict it to code sources
        // that you specify, because Thread.stop() is potentially unsafe.
        // See "http://java.sun.com/notes" for more information.
        permission java.lang.RuntimePermission "stopThread";

        // allows anyone to listen on un-privileged ports

        permission java.net.SocketPermission "localhost:1024-""listen";

        // "standard" properies that can be read by anyone


        permission java.util.PropertyPermission "java.version""read";
        permission java.util.PropertyPermission "java.vendor""read";
        permission java.util.PropertyPermission "java.vendor.url""read";
        permission java.util.PropertyPermission "java.class.version""read";
        permission java.util.PropertyPermission "os.name""read";
        permission java.util.PropertyPermission "os.version""read";
        permission java.util.PropertyPermission "os.arch""read";
        permission java.util.PropertyPermission "file.separator""read";
        permission java.util.PropertyPermission "path.separator""read";
        permission java.util.PropertyPermission "line.separator""read";

        permission java.util.PropertyPermission "java.specification.version""read";
        permission java.util.PropertyPermission "java.specification.vendor""read";
        permission java.util.PropertyPermission "java.specification.name""read";

        permission java.util.PropertyPermission "java.vm.specification.version""read";
        permission java.util.PropertyPermission "java.vm.specification.vendor""read";
        permission java.util.PropertyPermission "java.vm.specification.name""read";
        permission java.util.PropertyPermission "java.vm.version""read";
        permission java.util.PropertyPermission "java.vm.vendor""read";
        permission java.util.PropertyPermission "java.vm.name""read";
    };


    可以看到,JVM給沙箱內(nèi)的application分配的權(quán)限僅限于中止線程,監(jiān)聽1024以上的TCP端口,以及對一些系統(tǒng)屬性的讀取權(quán)限,像一般的socket操作和文件操作的權(quán)限都沒有。
         了解了安全管理器的概念以后我們回到授權(quán)問題上來。對用戶來說,最擔(dān)心的莫過于機器中病毒,病毒本質(zhì)上是一種惡意的程序,所以訪問控制首先是要對代碼的權(quán)限進行控制,上面我一直都在談Sandbox,也就是所謂的“沙箱”,熟悉Java安全性發(fā)展歷史的朋友大概對它不會陌生,初期的Java是采用這樣一種安全策略,即:本地代碼是可信的,而遠程代碼是不可信的,譬如applet是一種從網(wǎng)絡(luò)上下載到本地并在瀏覽器上運行的一段遠程代碼,因而是不可信的,所以早期的applet被完全置于Sandbox當(dāng)中,得到的權(quán)限是非常有限的;在1.0以后,直至Java 2出現(xiàn)之前,安全策略作了一些靈活的改變,applet不再是完全被歧視的“二等公民”了,因為有了簽名applet,用戶可以選擇信任這種經(jīng)過簽名的applet,從而applet也可以做一些以前被認為是“出格”的事情;到了Java 2,情況又變了,以前一向被信任的本地代碼似乎也變得不是那么可靠了,這還真說不準(zhǔn),難保誰不會在你出去跟女朋友逛街的時候,偷偷溜進來在你機器上拷個病毒什么的 ^_^ ,這樣本地代碼就落到了和遠程代碼相等同的地位了,這是比較符合現(xiàn)實世界場景的,在Java 2中的安全策略被稱之為“可配置的安全策略”,任何代碼,只要是通過安全管理器訪問,就必須為它預(yù)先設(shè)定好訪問權(quán)限,在這個之外的資源還是別的什么東東,對不起,

    java.security.AccessControlException: access denied……

    此路不通!


        簡單總結(jié)一下Java安全模型的發(fā)展史,大概就是下面的幾幅圖了:
    o_security1.0.gif


    o_security1.1.gif

    o_security1.2.gif
         由于現(xiàn)在普遍是多用戶的系統(tǒng),所以在實現(xiàn)代碼級訪問控制之外,我們還希望能夠?qū)τ脩舻男袨檫M行約束,因為對系統(tǒng)造成破壞的因素不僅僅是惡意代碼,人自身的有意或無意的不當(dāng)操作也會危及系統(tǒng),譬如向上面說的你不在的時候別人可以在你機器上拷病毒,如果系統(tǒng)能在你不在的時候也能拒絕這個家伙的登錄企圖,那樣麻煩豈不是少很多?于是在Java安全核心之外,提供了一個名為“Java認證與授權(quán)服務(wù)”(Java Authentication and Authorization Services,JAAS)東東,專門用來處理對用戶的認證和授權(quán),這也就是所謂的“以用戶為中心的授權(quán)模型”,說白了就是在“以代碼為中心的授權(quán)模型”上再加一層,首先用戶要獲得訪問權(quán)限,然后用戶去操縱代碼,代碼來實行真正的訪問操作。下面我主要是講講JAAS是如何工作的。

    2.了解幾個主要的API

         JAAS的API基本上位于javax.security.auth包及其下屬子包中,很容易找到的。

  • javax.security.auth.Subject
         Subject表征系統(tǒng)中一個認證的用戶,這個詞時而被譯為“主題”時而被以為“主體”(下面我要談到的Principal有時候也被譯為“主體”),不管它有幾個馬甲,反正你就可以看成是在Java中你這個人的影子,你對系統(tǒng)的訪問就體現(xiàn)為Subject.doAs()或Subject.doAsPrivileged()方法。

     

  • java.security.Principal
         Principal代表用戶的一種身份對象,一個用戶的身份可能不只一個,他所在的組或所擔(dān)任的角色也是一種身份,“張翠山”可以說“鐵劃銀鉤”,可以說“張三豐的徒弟”,可以說“張無忌他老爹”,我說“武當(dāng)七俠”甚至“武當(dāng)派”,當(dāng)然也沒錯,這是一個組,呵呵。通過一次登錄后,可能向Subject插入一個或多個Principal,這時候Subject才有實際意義,而不是一個空殼。

     

  • javax.security.auth.login.LoginContext
         LoginContext旨在提供一個開放的登錄總接口,你只需要用從策略文件中取得的策略名,以及下面介紹的回調(diào)對象創(chuàng)建得到一個LoginContext,再調(diào)用一次login()方法即可完成登錄,登錄模塊在這里是透明的。


  • javax.security.auth.spi.LoginModule
         登錄模塊實現(xiàn)了對用戶的認證邏輯,它的作用是在登錄配置文件中得到體現(xiàn),在后面的例子里我們會看到怎么編寫一個登錄配置文件以及上面說過的策略文件。LoginModule接口包括五個主要的方法:

            
    initialize方法,初始化模塊,保存當(dāng)前Subject以及一些參數(shù)。

            
    login方法,判斷一次登錄過程中是否認證通過。

            
    commit方法,是否提交登錄結(jié)果。咦,login不就行了嗎?干嗎要來個提交呢?這是因為JAAS采用的是類似于數(shù)據(jù)庫事務(wù)處理的過程,將整體登錄分為兩階段,盡管你login成功,但系統(tǒng)仍有權(quán)力根據(jù)你這次login的“地位”來決定究竟要不要接納你的身份,只有通過commit,用戶的Principal才會被真正添加到Subject當(dāng)中,哼哼,真陰險!這里所說的login的“地位”是指策略文件中登錄模塊的“控制標(biāo)記”選項,有點類似于優(yōu)先級的概念,因為登錄一個系統(tǒng)的過程可能會經(jīng)過不止一個登錄模塊,譬如我們登錄系統(tǒng)輸入口令,但這個口令可能保存在一個數(shù)據(jù)庫或LDAP目錄中,訪問這個數(shù)據(jù)源也需要經(jīng)過認證,這就不止一個登錄模塊了吧?所以我們需要分清哪些認證過程是重要的,哪些又是次要的,系統(tǒng)對用戶身份的接收與否是對這些策略綜合權(quán)衡的結(jié)果。

            
    abort方法:哎呀,上面解釋得是不是太多了?我們再看看abort,還記得數(shù)據(jù)庫事務(wù)處理的回退過程(roll back)嗎?abort就有點像roll back,表示系統(tǒng)并不接受你的身份,以前做過的統(tǒng)統(tǒng)作廢,現(xiàn)場又恢復(fù)到和登錄前完全一樣。

            
    logout方法:注銷過程,清除內(nèi)部狀態(tài),并刪除Subject中全部的Principal。

     

  • javax.security.auth.callback.CallbackHandler
         回調(diào)對象是JAAS中用以將交互過程和認證邏輯分離的一種機制,這也是符合OO和松散耦合(loosely coupled是一個時髦詞匯 ^_^)精神的。JAAS已經(jīng)實現(xiàn)了一些常用的回調(diào)對象,包括取得用戶名的NameCallback,取得口令的PasswordCallback,從終端獲得輸入文本的TextInputCallback,向終端發(fā)出文本消息的TextOutputCallback等等。我們所要做的僅僅是實現(xiàn)一個CallbackHandler接口,根據(jù)不同的交互信息類型,把從終端得到的信息填到相應(yīng)的Callback中去就行了。后面的例子我是用了一個JoptionPane提示文本框來輸入用戶名和口令的。

     

  • java.security.PrivilegedAction
          上面說了那么多登錄相關(guān)的接口,該說說授權(quán)了,如果我們只談寫源代碼,那么很簡單,只要實現(xiàn)一個PrivilegedAction接口,覆蓋一個run()方法,把你想要做的事情統(tǒng)統(tǒng)放到這個run中就可以了。但我說的只是寫源代碼部分,授權(quán)方面用得較多的還是在管理方面,譬如如何編寫一個策略文件,下面我們就來看看JAAS登錄和訪問控制的一個完整流程。

    3.基本流程
          JAAS被稱為是“可插拔的認證框架”(Pluggable Authentication Module,PAMs),其實PAM也不是SUN的專利,Linux上就有這方面的實現(xiàn),但PAM確實是較早用在了Solaris系統(tǒng)上。我們看看JAAS在認證和授權(quán)方面是怎么體現(xiàn)PAM思想的:

        主要包括這么幾個部分:
        用戶的Principal(MyPrincipal.class)
        登錄模塊(MyLoginModule.class)
        回調(diào)對象(MyCallbackHandler.class)
        訪問代碼(MyAction.class)
        系統(tǒng)入口(JAASTest.class)
        資源(myfile.txt)
        策略配置文件(login.conf)
        登錄配置文件(jaas.policy)
        啟動腳本(JAASTest.bat)

          由于啟動java的選項太長,所以寫了一個shell,在控制臺下運行JAASTest.bat,選項“-Djava.security.manager”指定啟用安全管理器,執(zhí)行的是JAASTest類的main線程,由于shell指定選項“-Djava.security.policy=jaas.policy”,該策略文件允許當(dāng)前代碼創(chuàng)建LoginContext,并授權(quán)進行其它一些操作,它首先初始化一個LoginContext,選項“-Djava.security.auth.login.config=login.conf”指定了登錄配置文件,所以在當(dāng)前目錄下找到文件login.conf,該文件中指定的登錄策略名稱為“JAASTest”,所以在LoginCotext中第一個參數(shù)也是“JAASTest”,同時使用我們自定義的回調(diào)對象MyCallbackHandler。創(chuàng)建LoginContext成功,可以進行登錄了,調(diào)用LoginContext的login方法,該方法找到login.conf中的登錄模塊MyLoginModule(當(dāng)然可以有若干個登錄模塊,這里我只用了一個),執(zhí)行該模塊的登錄過程,MyLoginModule首先初始化:

    Login module initializing ...


    并使用LoginContext所賦予它的回調(diào)對象MyCallbackHandler,該回調(diào)過程彈出兩個圖形對話框,要求輸入用戶名和口令,
    o_user.jpg                            o_password.jpg

    我們使用指定的用戶名“user”和口令“l(fā)etmepass”,確定以后分別傳給當(dāng)前的NameCallback和PasswordCallback,然后回到MyLoginModule的login過程,該過程從回調(diào)對象處得到NameCallback和PasswordCallback,進行認證(這里僅僅是簡單的用戶名和口令的對比),

    MyLoginModule: Authentication pass!


    并決定是否commit,由于在login.conf中定義該登錄模塊是required,所以是一個必須通過才能整體認證成功的模塊。

    MyLoginModule: Add a new principal to current subject.

    如果整體得到認證通過,那么Subject就可以授權(quán)允許MyAction中的代碼了,如語句Subject.doAs(…)所示,該代碼的動作是讀取當(dāng)前目錄下的myfile.txt文件,并將其內(nèi)容打印到控制臺,注意到在策略文件jaas.policy中賦予MyPrincipal身份對myfile.txt的讀取權(quán)限,所以我們成功看到控制臺下出現(xiàn)

    Access successfully! Reading file:
    ==================================
    Why?
    Because they care!
    Because they want to know the truth!
    Because they want their country back!
    Because it still belongs to us as long as the people have the guts to fight for what they believe in!

    ==================================

         這是我喜歡的一部經(jīng)典影片“JFK”中檢察官Garrison激情的最后陳詞中的一段,呵呵!
         以上過程我們可以用個圖表來表示:
    o_jaas.gif 
    4.簡單的例子

         以上流程中使用到的Java源代碼和配置文件如下:

    // MyPrincipal.java
    package com.jungleford.auth;
    
    import java.security.Principal;
    
    public class MyPrincipal implements Principal
    { // 一個Principal的例子
      private String name; // Principal的名字
    
      public MyPrincipal(String name)
      {
        this.name = name;
      }
    
      public String getName()
      { //取得Principal的名字
        return this.name;
      }
    
      public boolean equals(Object principal)
      { // 判斷兩個Pincipal相同的依據(jù)
        if (principal instanceof MyPrincipal)
          return this.name.equals(((MyPrincipal)principal).getName());
        else
          return false;
      }
    
      public String toString()
      { // Principal的表示
        return "MyPrincipal: " + this.name;
      }
    
      public int hashCode()
      { // 確定本對象的散列值
        // 用于有基于散列容器的場合,判斷在散列容器中是否是同一個對象。
        // 如果對hashCode感興趣,請參見:
        // http://www-900.ibm.com/developerWorks/cn/java/j-jtp05273/
        return this.name.hashCode();
      }
    }
    

    // MyLoginModule.java
    package com.jungleford.auth;
    
    import java.util.*;
    import java.io.IOException;
    import java.security.Principal;
    import javax.security.auth.*;
    import javax.security.auth.callback.*;
    import javax.security.auth.login.*;
    import javax.security.auth.spi.*;
    
    public class MyLoginModule implements LoginModule
    { // 一個登錄模塊的例子
      private Subject subject; // 登錄主體的表征
      private CallbackHandler cbHandler; // 回調(diào)對象,提供終端下獲取用戶名、口令的界面
      private Map sharedState; // 用于緩存中間結(jié)果的共享區(qū)
      private Map options; // 用于保存某些登錄模塊所需要用到的一些配置選項
    
      private boolean succeeded = false; // 一次login成功的標(biāo)志
      private boolean cmtSucceeded = false; // 整體登錄成功的提交標(biāo)志
    
      private String username; // 取得用戶名
      private char[] password; // 取得口令
    
      private Principal principal; // 取得登錄后的身份標(biāo)志
    
      public void initialize(Subject subject,
                               CallbackHandler cbHandler,
                               Map sharedState,
                               Map options)
      { // 初始化過程
        System.out.println("Login module initializing ...");
        System.out.println();
        this.subject = subject;
        this.cbHandler = cbHandler;
        this.sharedState = sharedState;
        this.options = options;
      }
    
      public boolean login() throws LoginException
      { // 一次登錄過程
        if (cbHandler == null) // 尚未配置回調(diào)對象
          throw new LoginException("Error: No CallbackHandler available " +
                                      "to garner authentication information from the user");
    
        Callback[] cbs = new Callback[2]; // 僅使用用戶名回調(diào)和口令回調(diào)
        cbs[0] = new NameCallback("Login: ");
        cbs[1] = new PasswordCallback("Password: ", false);
    
        try
        {
          cbHandler.handle(cbs);
          username = ((NameCallback)cbs[0]).getName();
          char[] temp = ((PasswordCallback)cbs[1]).getPassword();
          if (temp == null)
          { // 口令為空
            temp = new char[0];
          }
          password = new char[temp.length];
          System.arraycopy(temp, 0, password, 0, temp.length);
          ((PasswordCallback)cbs[1]).clearPassword(); // 清除內(nèi)存中的口令痕跡
        }
        catch (IOException ioe)
        {
          throw new LoginException(ioe.toString());
        }
        catch (UnsupportedCallbackException uce)
        {
          throw new LoginException("Error: " + uce.getCallback().toString() +
                                      " not available to garner authentication information " +
                                      "from the user");
        }
    
        boolean usrCorrect = false; // 用戶名正確否?
        boolean pwdCorrect = false; // 口令正確否?
        if (username.equals("user")) // 目前僅允許用戶名為user的登錄
          usrCorrect = true;
        if (usrCorrect &&
            password.length == 9 &&
            password[0] == 'l' &&
            password[1] == 'e' &&
            password[2] == 't' &&
            password[3] == 'm' &&
            password[4] == 'e' &&
            password[5] == 'p' &&
            password[6] == 'a' &&
            password[7] == 's' &&
            password[8] == 's') // user的口令指定為letmepass
        {
          System.out.println("MyLoginModule: Authentication pass!");
          System.out.println();
          pwdCorrect = true;
          succeeded = true;
          return true; // 一次登錄成功
        }
        else
        {
          System.out.println("MyLoginModule: Authentication failed!");
          System.out.println();
          succeeded = false;
          username = null;
          for (int i = 0; i < password.length; i++) // 清除內(nèi)存中的口令痕跡
            password[i] = ' ';
          password = null;
          if (!usrCorrect)
          {
            throw new FailedLoginException("Username incorrect!");
          }
          else
          {
            throw new FailedLoginException("Password incorrect!");
          }
        }
      }
    
      public boolean commit() throws LoginException
      { // 根據(jù)登錄配置策略判斷是否整體登錄成功
        if (succeeded == false)
        {
          return false;
        }
        else
        {
          principal = new MyPrincipal(username);
          if (!subject.getPrincipals().contains(principal))
          subject.getPrincipals().add(principal); // 把新的身份添加到subject中
    
          System.out.println("MyLoginModule: Add a new principal to current subject.");
          System.out.println();
    
          username = null;
          for (int i = 0; i < password.length; i++) // 清除內(nèi)存中的口令痕跡
            password[i] = ' ';
          password = null;
    
          cmtSucceeded = true;
          return true;
        }
      }
    
      public boolean abort() throws LoginException
      { // 放棄登錄,將狀態(tài)復(fù)位至登錄前
        if (succeeded == false)
        {
          return false;
        }
        else if (succeeded == true && cmtSucceeded == false)
        {
          succeeded = false;
          username = null;
          if (password != null)
          {
            for (int i = 0; i < password.length; i++) // 清除內(nèi)存中的口令痕跡
              password[i] = ' ';
            password = null;
          }
          principal = null;
        }
        else
        {
          logout();
        }
        return true;
      }
    
      public boolean logout() throws LoginException
      { // 注銷,并將狀態(tài)復(fù)位至登錄前
        subject.getPrincipals().remove(principal);
        succeeded = false;
        succeeded = cmtSucceeded;
        username = null;
        if (password != null)
        {
          for (int i = 0; i < password.length; i++) // 清除內(nèi)存中的口令痕跡
            password[i] = ' ';
          password = null;
        }
        principal = null;
        return true;
      }
    }
    

    // MyCallbackHandler.java
    package com.jungleford.auth;
    
    import java.io.IOException;
    import javax.security.auth.callback.*;
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    
    public class MyCallbackHandler implements CallbackHandler
    {
      public void handle(Callback[] cbs)
      throws IOException, UnsupportedCallbackException
      {
        String username =
        JOptionPane.showInputDialog(null,
                                      "<html>Available name: " +
                                      "<font color=\"blue\">user</font></html>",
                                      "Enter your name",
        JOptionPane.QUESTION_MESSAGE);
        String password =
        JOptionPane.showInputDialog(null,
                                      "<html>Available password: " +
                                      "<font color=\"blue\">letmepass</font></html>",
                                      "Enter your password",
        JOptionPane.QUESTION_MESSAGE);
        for (int i = 0; i < cbs.length; i++)
        {
          if (cbs[i] instanceof TextOutputCallback)
          {
            TextOutputCallback toc = (TextOutputCallback)cbs[i];
            switch (toc.getMessageType())
            {
              case TextOutputCallback.INFORMATION:
                System.out.println(toc.getMessage());
                break;
              case TextOutputCallback.ERROR:
                System.out.println("Error: " + toc.getMessage());
                break;
              case TextOutputCallback.WARNING:
                System.out.println("Warning: " + toc.getMessage());
                break;
              default:
                throw new IOException("Unsupported message type: " +
                                         toc.getMessageType());
            }
          }
          else if (cbs[i] instanceof NameCallback)
          {
            // prompt the user for a username
            NameCallback nc = (NameCallback)cbs[i];
            //System.err.print(nc.getPrompt());
            //System.err.flush();
            nc.setName(username);
          }
          else if (cbs[i] instanceof PasswordCallback)
          {
            // prompt the user for sensitive information
            PasswordCallback pc = (PasswordCallback)cbs[i];
            //System.err.print(pc.getPrompt());
            //System.err.flush();
            pc.setPassword(password.toCharArray());
          }
          else
          {
            throw new UnsupportedCallbackException(cbs[i], "Unrecognized Callback");
          }
        }
      }
    }
    

    //MyAction.java
    package com.jungleford.auth;
    
    import java.io.*;
    import java.security.*;
    
    public class MyAction implements PrivilegedAction
    { // 對資源的授權(quán)訪問動作
      public Object run()
      { // run方法是必須overriding的
        // 這里我們假設(shè)訪問動作是讀取當(dāng)前目錄下myfile.txt文件的內(nèi)容
        File file = new File("myfile.txt");
        String content = "";
        try
        {
          BufferedReader reader =
          new BufferedReader(
          new FileReader(file));
          String line = reader.readLine();
          while (line != null)
          {
            content += line + "\n";
            line = reader.readLine();
          }
        }
        catch (Exception e)
        {
          System.err.println("Error: Reading file failed!");
          System.err.println();
          e.printStackTrace();
        }
        return content;
      }
    }
    

    //JAASTest.java
    package com.jungleford.auth;
    
    import javax.security.auth.Subject;
    import javax.security.auth.login.LoginContext;
    
    public class JAASTest
    { // 測試我們JAAS登錄和授權(quán)的shell
      public static void main(String[] args)
      {
        LoginContext lc = null;
        try
        {
          // 創(chuàng)建context,使用自定義的回調(diào)對象,策略名為JAASTest
          // 簡單起見,僅使用一個MyLoginModule模塊
          lc = new LoginContext("JAASTest", new MyCallbackHandler());
        }
        catch (Exception e)
        {
          System.err.println("Error: Creating login context failed!");
          System.err.println();
          e.printStackTrace();
          System.exit(-1);
        }
        try
        { // 整體登錄
          lc.login();
        }
        catch (Exception e)
        {
          System.err.println("Error: Login failed!");
          System.err.println();
          e.printStackTrace();
          System.exit(-1);
        }
        // 獲得授權(quán)訪問
        Object object = Subject.doAs(lc.getSubject(), new MyAction());
        System.out.println("Access successfully! Reading file:");
        System.out.println("==================================");
        System.out.println(object);
        System.out.println("==================================");
        System.exit(0);
      }
    }
    

    //login.conf
    JAASTest
    {
    	com.jungleford.auth.MyLoginModule required;
    };
    

    //jaas.policy
    grant
    {
    	permission javax.security.auth.AuthPermission "createLoginContext";
    	permission javax.security.auth.AuthPermission "doAs";
    	permission javax.security.auth.AuthPermission "modifyPrincipals";
    	permission javax.security.auth.AuthPermission "getSubject";
    };
    
    grant principal com.jungleford.auth.MyPrincipal "guest"
    {
    	permission java.io.FilePermission "myfile.txt","read";
    };
    

    //JAASTest.bat
    java -Djava.security.manager
         -Djava.security.auth.login.config=login.conf
         -Djava.security.policy=jaas.policy
         com.jungleford.auth.JAASTest
    

    //myfile.txt
    Why?
    Because they care!
    Because they want to know the truth!
    Because they want their country back!
    Because it still belongs to us as long as the people have the guts to fight for what they believe in!
    

    參考資料

  • Java Security Architecture
  • Java 授權(quán)內(nèi)幕
  • Java安全性 第二部分:認證與授權(quán)
  • Java Security, 2nd Edition, by Scott Oaks
  • J2EE Security, by Pankaj Kumar
  • posted on 2005-04-02 16:20 jungleford 閱讀(472) 評論(0)  編輯  收藏 所屬分類: 咖啡屋 - Java 技術(shù)研究
    主站蜘蛛池模板: 久久精品成人免费国产片小草| 亚洲午夜av影院| 免费国产a理论片| 亚洲欧洲自拍拍偷综合| 亚洲七七久久精品中文国产| 午夜免费1000部| 在线观看免费视频一区| 国产AV无码专区亚洲AV蜜芽 | 亚洲最大的成人网| 亚洲成av人影院| 亚洲真人日本在线| 日韩成人在线免费视频| 两个人的视频高清在线观看免费| 免费在线看污视频| 一级成人a免费视频| 美美女高清毛片视频黄的一免费| 亚洲va久久久久| 亚洲人和日本人jizz| 综合自拍亚洲综合图不卡区| 亚洲乱色熟女一区二区三区丝袜| 免费v片在线观看| 美女黄网站人色视频免费国产| 中国xxxxx高清免费看视频| 久久99毛片免费观看不卡| 一区二区3区免费视频| 深夜特黄a级毛片免费播放| 亚洲AV色无码乱码在线观看| 男人天堂2018亚洲男人天堂| 91亚洲视频在线观看| 亚洲理论精品午夜电影| 亚洲欧洲综合在线| 亚洲伊人色一综合网| 亚洲国产成人精品无码区在线秒播 | 日本免费一区二区久久人人澡 | 亚洲中文字幕无码专区| 亚洲国产aⅴ综合网| 亚洲国产成人久久笫一页| 男人的天堂亚洲一区二区三区 | a级日本高清免费看| 国产羞羞的视频在线观看免费| 99免费精品视频|