1. 程式人生 > >編寫自己的登入與訪問控制模組

編寫自己的登入與訪問控制模組

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

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

1.Java的訪問控制機制

    談到訪問控制,或者說“授權”,這裡有兩層含義,一是從資源的角度,這個socket埠是否被允許操作?這個檔案是可讀的?可寫的?還是可執行的?還是以上都行?這就是我們在UNIX下用“ls ?l”命令列出當前目錄下檔案時,那些“-rwx-”之類的含義;二是從訪問者的角度,我想通過80埠看Web上新浪歐洲盃的新聞,在這個系統中有沒有這個資格?我想播放D盤上一個名為“friends.rm”的視訊檔案,我得到了訪問這個檔案的許可權了嗎?我有執行播放器的許可權嗎?
    Java在訪問控制策略上同時考慮了這兩方面內容,你說“不對呀,我用
FileOutputStream寫檔案,用Socket類連線遠端主機都用得好好的,沒什麼限制呀”,這我們得先談談什麼叫做“安全管理器”(SecurityManger)。安全管理器從JDK 1.0就開始有了,多古老啊!Java從設計的那一天開始就考慮了安全因素,安全管理器是Sandbox的最重要的一個部分,也是訪問控制的總協調者,我們能夠在通常情況下正常使用網路和檔案,那是因為當啟動application的時候(注意是application,不是applet!),如果你不加“-Djava.security.manager”選項,JVM是不會啟動Sandbox的,這時你可以“為所欲為”,而不會碰到
SecurityException之類的異常;一旦加入了“-Djava.security.manager”選項,你就會發現有一連串的異常出現嘍!

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


Java內建了一個預設的安全策略,這種情況下安全管理器首先裝載的是這個預設的策略,不信啊,不信你檢查一下你的“%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給沙箱內的application分配的許可權僅限於中止執行緒,監聽1024以上的TCP埠,以及對一些系統屬性的讀取許可權,像一般的socket操作和檔案操作的許可權都沒有。
    瞭解了安全管理器的概念以後我們回到授權問題上來。對使用者來說,最擔心的莫過於機器中病毒,病毒本質上是一種惡意的程式,所以訪問控制首先是要對程式碼的許可權進行控制,上面我一直都在談Sandbox,也就是所謂的“沙箱”,熟悉Java安全性發展歷史的朋友大概對它不會陌生,初期的Java是採用這樣一種安全策略,即:原生代碼是可信的,而遠端程式碼是不可信的,譬如applet是一種從網路上下載到本地並在瀏覽器上執行的一段遠端程式碼,因而是不可信的,所以早期的applet被完全置於Sandbox當中,得到的許可權是非常有限的;在1.0以後,直至Java 2出現之前,安全策略作了一些靈活的改變,applet不再是完全被歧視的“二等公民”了,因為有了簽名applet,使用者可以選擇信任這種經過簽名的applet,從而applet也可以做一些以前被認為是“出格”的事情;到了Java 2,情況又變了,以前一向被信任的原生代碼似乎也變得不是那麼可靠了,這還真說不準,難保誰不會在你出去跟女朋友逛街的時候,偷偷溜進來在你機器上拷個病毒什麼的 ^_^ ,這樣原生代碼就落到了和遠端程式碼相等同的地位了,這是比較符合現實世界場景的,在Java 2中的安全策略被稱之為“可配置的安全策略”,任何程式碼,只要是通過安全管理器訪問,就必須為它預先設定好訪問許可權,在這個之外的資源還是別的什麼東東,對不起,

java.security.AccessControlException: access denied……

此路不通!
    簡單總結一下Java安全模型的發展史,大概就是下面的幾幅圖了:
o_security1.0.gif

o_security1.1.gif

o_security1.2.gif

    由於現在普遍是多使用者的系統,所以在實現程式碼級訪問控制之外,我們還希望能夠對使用者的行為進行約束,因為對系統造成破壞的因素不僅僅是惡意程式碼,人自身的有意或無意的不當操作也會危及系統,譬如向上面說的你不在的時候別人可以在你機器上拷病毒,如果系統能在你不在的時候也能拒絕這個傢伙的登入企圖,那樣麻煩豈不是少很多?於是在Java安全核心之外,提供了一個名為“Java認證與授權服務”(Java Authentication and Authorization Services,JAAS)東東,專門用來處理對使用者的認證和授權,這也就是所謂的“以使用者為中心的授權模型”,說白了就是在“以程式碼為中心的授權模型”上再加一層,首先使用者要獲得訪問許可權,然後使用者去操縱程式碼,程式碼來實行真正的訪問操作。下面我主要是講講JAAS是如何工作的。

2.瞭解幾個主要的API


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

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

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

javax.security.auth.login. LoginContext
    LoginContext旨在提供一個開放的登入總介面,你只需要用從策略檔案中取得的策略名,以及下面介紹的回撥物件建立得到一個LoginContext,再呼叫一次 login ()方法即可完成登入,登入模組在這裡是透明的。

javax.security.auth.spi. LoginModule
    登入模組實現了對使用者的認證邏輯,它的作用是在登入配置檔案中得到體現,在後面的例子裡我們會看到怎麼編寫一個登入配置檔案以及上面說過的策略檔案。LoginModule介面包括五個主要的方法:

    
initialize 方法,初始化模組,儲存當前Subject以及一些引數。

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

    
commit 方法,是否提交登入結果。咦,login不就行了嗎?幹嗎要來個提交呢?這是因為JAAS採用的是類似於資料庫事務處理的過程,將整體登入分為兩階段,儘管你login成功,但系統仍有權力根據你這次login的“地位”來決定究竟要不要接納你的身份,只有通過commit,使用者的Principal才會被真正新增到Subject當中,哼哼,真陰險!這裡所說的login的“地位”是指策略檔案中登入模組的“控制標記”選項,有點類似於優先順序的概念,因為登入一個系統的過程可能會經過不止一個登入模組,譬如我們登入系統輸入口令,但這個口令可能儲存在一個數據庫或LDAP目錄中,訪問這個資料來源也需要經過認證,這就不止一個登入模組了吧?所以我們需要分清哪些認證過程是重要的,哪些又是次要的,系統對使用者身份的接收與否是對這些策略綜合權衡的結果。

    
abort 方法:哎呀,上面解釋得是不是太多了?我們再看看abort,還記得資料庫事務處理的回退過程(roll back)嗎?abort就有點像roll back,表示系統並不接受你的身份,以前做過的統統作廢,現場又恢復到和登入前完全一樣。

    
logout 方法:登出過程,清除內部狀態,並刪除Subject中全部的Principal。

javax.security.auth.callback. CallbackHandler
    回撥物件是JAAS中用以將互動過程和認證邏輯分離的一種機制,這也是符合OO和鬆散耦合(loosely coupled是一個時髦詞彙 ^_^)精神的。JAAS已經實現了一些常用的回撥物件,包括取得使用者名稱的 NameCallback ,取得口令的 PasswordCallback ,從終端獲得輸入文字的 TextInputCallback ,向終端發出文字訊息的 TextOutputCallback 等等。我們所要做的僅僅是實現一個CallbackHandler介面,根據不同的互動資訊型別,把從終端得到的資訊填到相應的 Callback 中去就行了。後面的例子我是用了一個 JoptionPane 提示文字框來輸入使用者名稱和口令的。

java.security. PrivilegedAction
    上面說了那麼多登入相關的介面,該說說授權了,如果我們只談寫原始碼,那麼很簡單,只要實現一個PrivilegedAction介面,覆蓋一個 run ()方法,把你想要做的事情統統放到這個run中就可以了。但我說的只是寫原始碼部分,授權方面用得較多的還是在管理方面,譬如如何編寫一個策略檔案,下面我們就來看看JAAS登入和訪問控制的一個完整流程。

3.基本流程


    JAAS被稱為是“可插拔的認證框架”(Pluggable Authentication Module,PAMs),其實PAM也不是SUN的專利,Linux上就有這方面的實現,但PAM確實是較早用在了Solaris系統上。我們看看JAAS在認證和授權方面是怎麼體現PAM思想的:

    主要包括這麼幾個部分:
    使用者的Principal(MyPrincipal.class)
    登入模組(MyLoginModule.class)
    回撥物件(MyCallbackHandler.class)
    訪問程式碼(MyAction.class)
    系統入口(JAASTest.class)
    資源(myfile.txt)
    策略配置檔案(login.conf)
    登入配置檔案(jaas.policy)
    啟動指令碼(JAASTest.bat)

    由於啟動java的選項太長,所以寫了一個shell,在控制檯下執行JAASTest.bat,選項“-Djava.security.manager”指定啟用安全管理器,執行的是JAASTest類的main執行緒,由於shell指定選項“-Djava.security.policy=jaas.policy”,該策略檔案允許當前程式碼建立LoginContext,並授權進行其它一些操作,它首先初始化一個LoginContext,選項“-Djava.security.auth.login.config=login.conf”指定了登入配置檔案,所以在當前目錄下找到檔案login.conf,該檔案中指定的登入策略名稱為“JAASTest”,所以在LoginCotext中第一個引數也是“JAASTest”,同時使用我們自定義的回撥物件MyCallbackHandler。建立LoginContext成功,可以進行登入了,呼叫LoginContext的login方法,該方法找到login.conf中的登入模組MyLoginModule(當然可以有若干個登入模組,這裡我只用了一個),執行該模組的登入過程,MyLoginModule首先初始化:

Login module initializing ...


並使用LoginContext所賦予它的回撥物件MyCallbackHandler,該回調過程彈出兩個圖形對話方塊,要求輸入使用者名稱和口令,
o_user.jpg

o_password.jpg

我們使用指定的使用者名稱“user”和口令“letmepass”,確定以後分別傳給當前的NameCallback和PasswordCallback,然後回到MyLoginModule的login過程,該過程從回撥物件處得到NameCallback和PasswordCallback,進行認證(這裡僅僅是簡單的使用者名稱和口令的對比),

MyLoginModule: Authentication pass!


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

MyLoginModule: Add a new principal to current subject.

 

如果整體得到認證通過,那麼Subject就可以授權允許MyAction中的程式碼了,如語句Subject.doAs(…)所示,該程式碼的動作是讀取當前目錄下的myfile.txt檔案,並將其內容列印到控制檯,注意到在策略檔案jaas.policy中賦予MyPrincipal身份對myfile.txt的讀取許可權,所以我們成功看到控制檯下出現

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!

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

    這是我喜歡的一部經典影片“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相同的依據

    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; // 回撥物件,提供終端下獲取使用者名稱、口令的介面

  private Map sharedState; // 用於快取中間結果的共享區
  private Map options; // 用於儲存某些登入模組所需要用到的一些配置選項

  private boolean succeeded = false; // 一次login成功的標誌

  private boolean cmtSucceeded = false; // 整體登入成功的提交標誌

  private String username; // 取得使用者名稱
  private char[] password; // 取得口令


  private Principal principal; // 取得登入後的身份標誌

  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) // 尚未配置回撥物件
      throw new LoginException("Error: No CallbackHandler available " +
                                  "to garner authentication information from the user");

    Callback[] cbs = new Callback[2]; // 僅使用使用者名稱回撥和口令回撥

    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(); // 清除記憶體中的口令痕跡

    }
    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++) // 清除記憶體中的口令痕跡

        password[i] = ' ';
      password = null;
      if (!usrCorrect)
      {
        throw new FailedLoginException("Username incorrect!");
      }
      else
      {
        throw new FailedLoginException("Password incorrect!");
      }
    }
  }

  public boolean commit() throws LoginException
  { // 根據登入配置策略判斷是否整體登入成功

    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++) // 清除記憶體中的口令痕跡

        password[i] = ' ';
      password = null;

      cmtSucceeded = true;
      return true;
    }
  }

  public boolean abort() throws LoginException
  { // 放棄登入,將狀態復位至登入前

    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++) // 清除記憶體中的口令痕跡

          password[i] = ' ';
        password = null;
      }
      principal = null;
    }
    else
    {
      logout();
    }
    return true;
  }

  public boolean logout() throws LoginException
  { // 登出,並將狀態復位至登入前

    subject.getPrincipals().remove(principal);
    succeeded = false;
    succeeded = cmtSucceeded;
    username = null;
    if (password != null)
    {
      for (int i = 0; i < password.length; 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
// 對資源的授權訪問動作

  public Object run()
  { // run方法是必須overriding的

    // 這裡我們假設訪問動作是讀取當前目錄下myfile.txt檔案的內容
    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登入和授權的shell

  public static void main(String[] args)
  {
    LoginContext lc = null;
    try
    {
      // 建立context,使用自定義的回撥物件,策略名為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);
    }
    // 獲得授權訪問

    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 授權內幕

Java安全性 第二部分 認證與授權

Java Security, 2nd Edition , by Scott Oaks

J2EE Security , by Pankaj Kumar
           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述