Axis高度応用(二)


前編ではHandlerの応用を簡単に紹介し、本章では前編に基づいてAuthentizationという概念を加え、Handlerの使用を詳しく紹介する.
 
Handlerの基本概念J 2 EE WebサービスにおけるHandler技術の特徴はサーブレット技術におけるFilterによく似ている.サーブレットでは、1つのHTTPがサービス側に到達すると、複数のFilterを介して要求をフィルタリングし、サービスを提供するサーブレットに到達することがよく知られています.これらのFilterの機能は、要求を統一的に符号化し、ユーザーを認証し、ユーザーの質問をシステムログに書き込むなどします.したがって、WebサービスにおけるHandlerは、通常、以下の機能を提供する.
クライアントを認証し、授権する.ユーザーのアクセスをシステムログに書き込む.要求されたSOAPメッセージを暗号化し、復号する.Web Servicesオブジェクトをキャッシュします.SOAPメッセージHandlerは、RPC要求または応答を表すSOAPメッセージにアクセスすることができる.JAX−RPC技術では、SOAPメッセージHandlerをサービス側に配置したり、クライアントで使用したりすることができる.
 
SOAPメッセージHandler処理手順
オンライン決済サービスでは、要求および応答のSOAPメッセージを暗号化するために、不正なユーザがサービス側およびクライアントから送信される情報にアクセスまたは変更することを防止する必要がある.クライアントがSOAPメッセージを送信すると、Handlerは要求メッセージの中のいくつかの機密情報(クレジットカードパスワードなど)を暗号化し、暗号化されたSOAPメッセージをサービス側に送信する.サービス側のSOAPメッセージHandlerはクライアントの要求を切り取り,要求されたSOAPメッセージを復号し,復号後のSOAPメッセージをターゲットのWebサービスエンドポイントに配信する.
 
Apache axisは私たちがWebサービスを開発するのに良い選択です(もちろんaxis 2、Metroの2つのより優れた安全なエンジンを使用することもできます)、axisWebサービス開発ツールを使用して、Handlerを使用してサービス側の要求と応答を処理することができます.典型的には、ピボットポイント(pivot point)は、Apacheがプロバイダ機能に相当する部分であり、これによってターゲットのWebサービスとインタラクティブになり、一般的にProviderと呼ばれる.axisでよく使われるProviderはJava:RPC,java:MSG,java:EJBです.1つのWebサービスは、1つまたは複数のHandlerを配備することができる.
 
Apache axisのHandlerアーキテクチャとJAX-RPC 1.0(JSR 101)のアーキテクチャは少し異なりますが、本明細書のコードはaxisで開発されているため、axis環境で実行する必要があることを宣言する必要があります.
 
次のUserAuthentizationHandlerクラスは、簡単なユーザー検証プロセスを示しています.
public class UserAuthentizationHandler extends BasicHandler {
	public void invoke(MessageContext msgContext) throws AxisFault {
		SecurityProvider provider = (SecurityProvider) msgContext.getProperty("securityProvider");
		if (provider == null) {
			provider = new SimpleSecurityProvider();
			msgContext.setProperty("securityProvider", provider);
		}
		if (provider != null) {
			String userId = msgContext.getUsername();
			//        ,  authUser==null,        ,  Server.Unauthenticated  。
			org.apache.axis.security.AuthenticatedUser authUser = provider.authenticate(msgContext);
			if (authUser == null)
				throw new AxisFault("Server.Unauthenticated", Messages.getMessage("cantAuth01", userId), null, null);
			//       ,             。
			msgContext.setProperty("authenticatedUser", authUser);
			System.out.println( authUser.getName() + "       ! ");
		}
	}
}

 deploy.wsddでの説明
<!-- UserAuthentization     -->
<handler name="UserAuthentization" type="java:handler.UserAuthentizationHandler">
</handler>

 server-config.wsdd
<handler name="UserAuthentization" type="java:handler.UserAuthentizationHandler"/>
<service name="MyService" provider="java:RPC">
  <requestFlow>
   <handler type="Log"/>
   <handler type="UserAuthentization"/>
  </requestFlow>
  <parameter name="allowedMethods" value="*"/>
  <parameter name="scope" value="session"/>
  <parameter name="className" value="myservice.MyService"/>
 </service>

コードからこのような言葉を見つけることができます
String userId = msgContext.getUsername(); 

SOapメッセージからユーザ名を取得する.パスワードはどこで取るのかと思う人がいます.実はsoapメッセージにはクライアントから送られてきたパスワードも含まれています.
msgContext.getPassword();

方法取得では、ユーザー名とパスワードはどこで設定されていますか?次に、クライアントがどのように呼び出されたかを見てみましょう.
public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {

		//   service  URL

		String endpoint = "http://localhost:" + "8080"
				+ "/axis_example/services/MyService";

		//       (service)  (call)

		Service service = new Service();

		Call call = (Call) service.createCall();//   service  call  

		//   service  URL

		call.setTargetEndpointAddress(new java.net.URL(endpoint));

		//    (processService) MyService.java       

		call.setOperationName("processService");
		/********************************************************/
		//               
		call.getMessageContext().setUsername("sosdairs");
		call.getMessageContext().setPassword("123456");
                                /********************************************************/
		// Object        ,   "This is Test!",  processService(String arg)

		String ret = (String) call.invoke(new Object[] { "This is Test!", "" });

		System.out.println(ret);

	}
}

クライアントプログラムは、サービス側メソッドを呼び出す前に、soapにUsernameとPassnameを設定.サービス側U s e rAuthentizationHandlerクラスでproviderを呼び出す.authenticate(msgContext)このメソッドクラスはクライアントのアイデンティティを検証する.このプロセスはリモートログインのプロセスであり、クライアントはユーザー名のパスワードを着て、サービス側が検証することを想像することができる.次に、SecurityProviderを見てみましょう.SecurityProviderはインタフェースであり、SimpleSecurityProviderクラスによってインタフェースを実現する方法です.
public class SimpleSecurityProvider implements SecurityProvider {
    protected static Log log =
        LogFactory.getLog(SimpleSecurityProvider.class.getName());

    HashMap users = null;
    HashMap perms = null;

    boolean initialized = false;

    // load the users list
    private synchronized void initialize(MessageContext msgContext)
    {
        if (initialized) return;

        String configPath = msgContext.getStrProp(Constants.MC_CONFIGPATH);
        if (configPath == null) {
            configPath = "";
        } else {
            configPath += File.separator;
        }
        File userFile = new File(configPath + "users.lst");
        if (userFile.exists()) {
            users = new HashMap();

            try {

                FileReader        fr   = new FileReader( userFile );
                LineNumberReader  lnr  = new LineNumberReader( fr );
                String            line = null ;

                // parse lines into user and passwd tokens and add result to hash table
                while ( (line = lnr.readLine()) != null ) {
                    StringTokenizer  st = new StringTokenizer( line );
                    if ( st.hasMoreTokens() ) {
                        String userID = st.nextToken();
                        String passwd = (st.hasMoreTokens()) ? st.nextToken() : "";
                        users.put(userID, passwd);
                    }
                }

                lnr.close();

            } catch( Exception e ) {
                log.error( Messages.getMessage("exception00"), e );
                return;
            }
        }
        initialized = true;
    }

    /** Authenticate a user from a username/password pair.
     *
     * @param username the user name to check
     * @param password the password to check
     * @return an AuthenticatedUser or null
     */
    public AuthenticatedUser authenticate(MessageContext msgContext) {

        if (!initialized) {
            initialize(msgContext);
        }

        String username = msgContext.getUsername();
        String password = msgContext.getPassword();

            // in order to authenticate, the user must exist
            if ( username == null ||
                 username.equals("") ||
                 !users.containsKey(username) )
                return null;

            String valid = (String) users.get(username);

            // if a password is defined, then it must match
            if ( valid.length()>0 && !valid.equals(password) )
                return null;

            return new SimpleAuthenticatedUser(username);
        }

        return null;
    }

}

authenticateメソッドでは、msgContextからクライアントからのusernameを取得する、passwordはusersに対応するkey-valueと比較する.usersはHashMapで、初期化方法でusersを読む.lstこのファイルこれにより、サービス側にusersを1つ保存する必要があることがわかる.lstファイルで、ユーザー名とパスワードを保存します.アプリケーションの*/WEB-INF/ディレクトリの下にusersを作成する.lstファイル、内容は
sosdairs 123456
test 123456
tester 123456 

サービスを開始し、Clientプログラムを実行します.logファイルにはアクセス情報が表示されます.クライアントが設定したユーザー名のパスワードが正しくない場合は、Unauthenticatedエラーメッセージが返されます.
 
上記のユーザー認証があれば、私たちのサービスは安全ですか?答えは否定的だ.クライアントがサービス側にアクセスするサービスを検証する必要がある場合があります.これはライセンスとも呼ばれます.
このような要求があると仮定すると、1台のホストは複数のサービスを提供し、これらのサービスはいずれも訪問者にアイデンティティ確認(上述)を要求し、そのうち一部のサービスは検証されたプログラムを通じてすべてアクセスすることができ、一部のサービスは特定のアイデンティティの訪問者にのみ開放される.以上のヒントがあれば、Handlerを作ってサービスにアクセスすることもできます.
 
axisはSimpleSecurityProviderというクラスを提供し、このクラスでサービス認証を行うことができる.展示を容易にするために、私はこのような小さな修正をして、私のアプリケーションに置いて、SimpleSecurityProviderクラスを直接使用していません.コードを見てください
/**
 *        axis-1_4\src\org\apache\axis\security\simple\SimpleSecurityProvider.java
 * @author sosdairs
 *
 */
public class RightAuthorizationHandler extends BasicHandler {
	public void invoke(MessageContext msgContext) throws AxisFault {
		boolean allowByDefault = JavaUtils
				.isTrueExplicitly(getOption("allowByDefault"));

		//        
		AuthenticatedUser user = (AuthenticatedUser) msgContext
				.getProperty(MessageContext.AUTHUSER);

		if (user == null)
			throw new AxisFault("Server.NoUser", Messages
					.getMessage("needUser00"), null, null);

		String userID = user.getName();
		Handler serviceHandler = msgContext.getService();

		if (serviceHandler == null)
			throw new AxisFault(Messages.getMessage("needService00"));

		String serviceName = serviceHandler.getName();
		//           
		String allowedRoles = (String) serviceHandler.getOption("allowedRoles");
		if (allowedRoles == null) {
			if (allowByDefault) {
				System.out.println(" noRoles00 ");
			} else {
				throw new AxisFault("Server.Unauthorized", Messages.getMessage(
						"notAuth00", userID, serviceName), null, null);
			}
			return;
		}

		SecurityProvider provider = (SecurityProvider) msgContext
				.getProperty(MessageContext.SECURITY_PROVIDER);
		if (provider == null)
			throw new AxisFault(Messages.getMessage("noSecurity00"));

		StringTokenizer st = new StringTokenizer(allowedRoles, ",");
		while (st.hasMoreTokens()) {
			String thisRole = st.nextToken();
			//         
			if (provider.userMatches(user, thisRole)) {
				System.out.println(user.getName() + "       "
						+ serviceHandler.getName() + "    ");
				return;
			}
		}

		throw new AxisFault("Server.Unauthorized", Messages.getMessage(
				"cantAuth02", userID, serviceName), null, null);
	}
}

 deploy.wsdd
<!-- Authorization                 -->
<handler name="RightAuthorization" type="java:handler.RightAuthorizationHandler">	
</handler>  <service name="MyService" provider="java:RPC">
  <parameter name="className" value="myservice.MyService" />
  <parameter name="allowedMethods" value="*" />
  <!--      sosdairs,test     -->
  <parameter name="allowedRoles" value="test,sosdairs"/>  
  <!-- webservice    object      -->
  <!-- request, session, application -->
  <parameter name="scope" value="session"/>
  <requestFlow>  
         <handler type="Log"/>
         <handler type="UserAuthentization"/>
         <handler type="RightAuthorization"/>         
      </requestFlow>  
 </service>

 server-config.wsdd
<handler name="RightAuthorization" type="java:handler.RightAuthorizationHandler"/>
<service name="MyService" provider="java:RPC">
  <requestFlow>
   <handler type="Log"/>
   <handler type="UserAuthentization"/>
   <handler type="RightAuthorization"/>
  </requestFlow>
  <parameter name="allowedRoles" value="test,sosdairs"/>
  <parameter name="allowedMethods" value="*"/>
  <parameter name="scope" value="session"/>
  <parameter name="className" value="myservice.MyService"/>
</service>

Client.JAvaでは、ユーザ名のパスワードをtester 123456に設定Clientを実行し、サービスはそのアイデンティティ確認が可能であるが、サービスにアクセスできない.
これで、私たちのサービスはいくつかのセキュリティを備えています.もちろん、このセキュリティは非常に低いです.SOAPメッセージはhttpプロトコル上で伝送する、soapはXMLの組織形式であり、いずれも明文伝送を採用する.
何?明文!さっき送ったユーザー名のパスワードは他の人に見られたのではないでしょうか.そうですね.見られたのです.だから私たちのサービスは安全ではありません.
少しずつ改善しましょう.ユーザ名のパスワードを保護するためである.ユーザ名、パスワードを暗号化して転送することができます.これはいい考えです.暗号解読ユーザ名のパスワードを暗号解読するだけでなく、SOAPメッセージ全体を処理することもできる.クライアント側もサービス側も特定のアルゴリズムを実現できればよい.WEBセキュリティ技術については、多くの方法で実現できます.
     WS-Security
     WS-Trace
XML Digital Signature(XMLデジタル署名)XML Encryption(XML暗号化)XKMS(XML KeyManagement Specification)XACML(eXtensible Access Control Markup Language)SAML(Secure Assertion Markup Language)ebXML Message Service Security Identity Management&Liberty Project
SSL/HTTPSプロトコルを使用して転送
この章はここでただ言及するだけで、後の章で述べられますので、注目してください.
 
最後に、chainと知り合ってみましょう.
Chainが実現する一連のHandlerの機能は、複数のHandlerの集合であると理解できる.
package chain;

import handler.RightAuthorizationHandler;
import handler.UserAuthentizationHandler;

import org.apache.axis.SimpleChain;

public class SecurityChain extends SimpleChain{
    private static final long serialVersionUID = 1L;   
    public SecurityChain(){   
    	UserAuthentizationHandler user = new UserAuthentizationHandler();   
        RightAuthorizationHandler right = new RightAuthorizationHandler();   
        this.addHandler(user);   
        this.addHandler(right);   
    } 
}

SimpleChainはBasicHandlerを継承し、SimpleChainもHandlerと言える
public class SimpleChain extends BasicHandler implements Chain {
	.......
}

 deploy.wsdd
<chain name="SecurityChain" >
        <handler type="java:chain.SecurityChain"/>  		
</chain>
<requestFlow>
      <chain type="SecurityChain"/>  
<!--  <handler type="Log"/>-->
<!--  <handler type="UserAuthentization"/>-->
<!--  <handler type="RightAuthorization"/>-->
</requestFlow>

ここではUserAuthentizationとRightAuthorizationをSecurityChainの構造方法に入れただけで、サービスがリリースされると自動的にHandlerがロードされます.
 
 
上记の详しい绍介を通じて、Handlerの使い方をマスターできるはずです.また、これらの用法は基本的に安全性の高い需要を満たすことができる.次の章では、安全なAXISサービスの構築方法について説明します.