How Tomcat Works(十三)

20401 ワード

本文はtomcatコンテナの安全管理を分析し、servlet技術は配置記述器(web.xmlファイル)を構成することによって制限されたコンテンツにアクセス制御を行うことをサポートする.servletコンテナは、認証器というバルブによってセキュリティ制限をサポートし、servletコンテナが起動すると、Contextコンテナのパイプに認証器バルブが追加されます.Wrapperバルブを呼び出す前に、ベリファイアバルブを呼び出し、現在のユーザーを認証します.検証弁はContextコンテナのRealmオブジェクトのauthenticate()メソッドを呼び出し、ユーザーが入力したユーザー名とパスワードを入力してユーザーの認証を行います.
Realmオブジェクトは、ユーザーの認証に使用されるコンポーネントであり、ユーザーが入力したユーザー名とパスワードのペアの有効性を判断し、通常はContextコンテナに関連付けられます.ではRealmオブジェクトはどのようにしてユーザーのアイデンティティを検証しますか?実際には、有効なすべてのユーザーのユーザー名とパスワードのペアが保存されているか、これらのデータを格納するメモリにアクセスします.これらのデータの具体的な格納はRealmオブジェクトの具体的な実装に依存し、tomcatでは有効なユーザ情報はデフォルトでtomcat-userに格納.xmlファイル;もちろん、リレーショナル・データベースなどの他のリソース検証Realmオブジェクトを使用して実装することもできます.
tomcatではRealmオブジェクトはorg.apache.catalina.Realmインタフェースの例です.検証に関連する方法は次のとおりです.
public Principal authenticate(String username, String credentials);
public Principal authenticate(String username, byte[] credentials);
public Principal authenticate(String username, String digest,                                  String nonce, String nc, String cnonce,                                  String qop, String realm,                                  String md5a2);
public Principal authenticate(X509Certificate certs[]);
通常、最初のリロード方法が使用されます.Realmインタフェースには、次のように署名するhasRole()メソッドがあります.
public boolean hasRole(Principal principal, String role);
さらに、RealmインタフェースのgetContainer()メソッドとsettContainer()メソッドは、RealmインスタンスをContextインスタンスに関連付けるために使用されます.
tomcatではRealmインタフェースの基本的な実装形式はorgである.apache.catalina.realm.抽象クラスであるRealmBaseクラスorg.apache.catalina.realmパッケージには、JDBCRealm、JNDIrealm、MemoryRealm、UserDatabaseRealmクラスなど、RealmBaseクラスの継承クラスの実装もいくつか用意されています.デフォルトでは、MemoryRealmクラスのインスタンスが検証用のRealmオブジェクトとして使用されます.MemoryRealmインスタンスが最初に呼び出されるとtomcat-userが読み出されます.xmlドキュメントの内容.
tomcatでjava.security.Principalインタフェースの例はorgである.apache.catalina.realm.GenericPrincipalクラス、GenericPrincipalインスタンスは、2つのコンストラクション関数に示すように、常に1つのRealmオブジェクトに関連付けられる必要があります.
public GenericPrincipal(Realm realm, String name, String password) {        this(realm, name, password, null);    }
public GenericPrincipal(Realm realm, String name, String password,                            List roles) {        super();        this.realm = realm;        this.name = name;        this.password = password;        if (roles != null) {            this.roles = new String[roles.size()];            this.roles = (String[]) roles.toArray(this.roles);            if (this.roles.length > 0)                Arrays.sort(this.roles);        }    }
GenericPrincipalインスタンスには、ユーザー名とパスワードのペアが必要です.また、このユーザー名とパスワードは、対応するロールリストに対してオプションです.次にhasRole()メソッドを呼び出し、文字列形式のロール名を入力してPrincipalオブジェクトに指定したロールがあるかどうかを確認できます.
public boolean hasRole(String role) {        if (role == null)            return (false);        return (Arrays.binarySearch(roles, role) >= 0);    }
簡単なRealmオブジェクトがどのように動作するかを見てみましょう.ハードコーディングで2つのユーザー名とパスワードが保存されています.
SimpleRealmクラスのソースコードは以下の通りです(無関係コードを省略します)
public class SimpleRealm implements Realm {

  public SimpleRealm() {
    createUserDatabase();
  }

  private Container container;
  private ArrayList users = new ArrayList();

  public Container getContainer() {
    return container;
  }

  public void setContainer(Container container) {
    this.container = container;
  }
  /**
   *         ,  Principal    
   */
  public Principal authenticate(String username, String credentials) {
    System.out.println("SimpleRealm.authenticate()");
    if (username==null || credentials==null)
      return null;
    User user = getUser(username, credentials);
    if (user==null)
      return null;
    return new GenericPrincipal(this, user.username, user.password, user.getRoles());
  }
  /**
  *   Principal            
  */
  public boolean hasRole(Principal principal, String role) {
    if ((principal == null) || (role == null) ||
      !(principal instanceof GenericPrincipal))
      return (false);
    GenericPrincipal gp = (GenericPrincipal) principal;
    if (!(gp.getRealm() == this))
      return (false);
    boolean result = gp.hasRole(role);
    return result;
  }


  private User getUser(String username, String password) {
    Iterator iterator = users.iterator();
    while (iterator.hasNext()) {
      User user = (User) iterator.next();
      if (user.username.equals(username) && user.password.equals(password))
        return user;
    }
    return null;
  }

  private void createUserDatabase() {
    User user1 = new User("ken", "blackcomb");
    user1.addRole("manager");
    user1.addRole("programmer");
    User user2 = new User("cindy", "bamboo");
    user2.addRole("programmer");

    users.add(user1);
    users.add(user2);
  }

  class User {

    public User(String username, String password) {
      this.username = username;
      this.password = password;
    }

    public String username;
    public ArrayList roles = new ArrayList();
    public String password;

    public void addRole(String role) {
      roles.add(role);
    }
    public ArrayList getRoles() {
      return roles;
    }
  }

}

その中のauthenticate()メソッドはベリファイアによって呼び出され、ユーザーが提供したユーザー名とパスワードが無効である場合nullが返されます.そうでない場合、そのユーザーを表すPrincipalオブジェクトが返されます.
上記のセクションではRealmオブジェクトについて説明し、次にベリファイアに関する実装について説明します.ベリファイアはorg.apache.catalina.Authenticatorインタフェースの例で、Authenticatorインタフェースは識別インタフェースであり、メソッドは宣言されていない.tomcatではAuthenticatorインタフェースの基本的な実装orgが提供する.apache.catalina.authenticator.AuthenticatorBaseクラス、AuthenticatorBaseクラスはorgも継承する.apache.catalina.valves.ValveBaseクラス、つまりAuthenticatorBaseクラスもバルブです.tomatでは、BasicAuthenticatorクラス、FormAuthenticatorクラス、DigestAuthenticatorクラス、SSLAuthenticatorクラスなど、多くのAuthenticatorBaseクラスの継承クラスが提供されています.さらにtomcatユーザが認証メソッド名を指定していない場合、NonLogingAuthenticatorクラスは、訪問者の認証に使用されます.
AuthenticatorBaseクラスのinvoke()メソッドはauthenticate()抽象メソッドを呼び出し、後者の実装はサブクラスに依存し、ここではtempletメソッドモードと同様である.
では、私たちのウェブアプリケーションでは、具体的にその検証器を採用して実現しています.これは私たちがウェブに依存しています.xmlファイルの構成(構成例は以下の通り)
    <web-app>  
    <security-constraint>  
      <web-resource-collection>  
         <web-resource-name>  
            Member Area  
         </web-resource-name>  
         <description>  
            Only registered members can access this area.  
         </description>  
         <url-pattern>/member/*</url-pattern>  
         <http-method>GET</http-method>  
         <http-method>POST</http-method>  
      </web-resource-collection>  
      <auth-constraint>  
         <role-name>member</role-name>  
      </auth-constraint>  
    </security-constraint>  
    <login-config>  
      <auth-method>BASIC</auth-method>  
    </login-config>  
    <security-role>  
      <role-name>member</role-name>  
    </security-role>  
    </web-app>  

上記の例ではBASIC検証を採用していますが、FORM、DIGESTまたはCLIENT-CERTなどに設定して、それぞれ異なるベリファイアクラス(BasicAuthenticatorクラス、FormAuthenticatorクラス、DigestAuthenticatorクラス、SSLAuthenticatorクラス)に対応し、auth-method要素が設定されていない場合、LoginConfigオブジェクトauth-method属性のデフォルト値はNONEとなります.NonLoginAuthenticatorを使用してセキュリティ検証を行います.(注:LoginConfigオブジェクトにはRealmオブジェクト名と使用する認証方法がカプセル化されています)
最後に、Contextコンテナインスタンスがベリファイアバルブをどのように使用するかについて説明します.ここでは、ContextコンテナインスタンスとしてのLifecycleEvent()でauthenticatorConfig()メソッドを呼び出してBasicAuthenticatorクラスをインスタンス化し、StandardContextインスタンスのパイプにバルブとして追加するSimpleContextConfigクラスについて説明します.
次はSimpleContextConfigクラスのauthenticatorConfig()メソッド実装です.
private synchronized void authenticatorConfig() {
    // Does this Context require an Authenticator?
    SecurityConstraint constraints[] = context.findConstraints();
    if ((constraints == null) || (constraints.length == 0))
      return;
    LoginConfig loginConfig = context.getLoginConfig();
    if (loginConfig == null) {
      loginConfig = new LoginConfig("NONE", null, null, null);
      context.setLoginConfig(loginConfig);
    }

    // Has an authenticator been configured already?
    Pipeline pipeline = ((StandardContext) context).getPipeline();
    if (pipeline != null) {
      Valve basic = pipeline.getBasic();
      if ((basic != null) && (basic instanceof Authenticator))
        return;
      Valve valves[] = pipeline.getValves();
      for (int i = 0; i < valves.length; i++) {
        if (valves[i] instanceof Authenticator)
        return;
      }
    }
    else { // no Pipeline, cannot install authenticator valve
      return;
    }

    // Has a Realm been configured for us to authenticate against?
    if (context.getRealm() == null) {
      return;
    }

    // Identify the class name of the Valve we should configure
    String authenticatorName = "org.apache.catalina.authenticator.BasicAuthenticator";
    // Instantiate and install an Authenticator of the requested class
    Valve authenticator = null;
    try {
      Class authenticatorClass = Class.forName(authenticatorName);
      authenticator = (Valve) authenticatorClass.newInstance();
      ((StandardContext) context).addValve(authenticator);
      System.out.println("Added authenticator valve to Context");
    }
    catch (Throwable t) {
    }
  }

--------------------------------------------------------------------------- 
本シリーズHow Tomcat Works系本人オリジナル
転載は出典のブログ園のハリネズミのおとなしいことを明記してください
本人メールアドレス:chenying 998179#163.com(#を@)に変更
このリンクhttp://www.cnblogs.com/chenying99/p/3242277.html