7

私はこれに頭を悩ませています:インターセプターを使用していくつかのSOAPヘッダーをチェックしますが、インターセプターチェーンを中止しても、ユーザーにエラーで応答するにはどうすればよいですか?

フォールトのスローは出力に関して機能しますが、リクエストはまだ処理中であり、メッセージコンテキストですべてのサービスにフラグをチェックさせたくありません。

「message.getInterceptorChain()。abort();」で中止 実際にはすべての処理を中止しますが、クライアントに返されるものもありません。

行く正しい方法は何ですか?

public class HeadersInterceptor extends AbstractSoapInterceptor {

    public HeadersInterceptor() {
        super(Phase.PRE_LOGICAL);
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        Exchange exchange = message.getExchange();
        BindingOperationInfo bop = exchange.getBindingOperationInfo();
        Method action = ((MethodDispatcher) exchange.get(Service.class)
                .get(MethodDispatcher.class.getName())).getMethod(bop);

        if (action.isAnnotationPresent(NeedsHeaders.class)
                && !headersPresent(message)) {
            Fault fault = new Fault(new Exception("No headers Exception"));
            fault.setFaultCode(new QName("Client"));

            try {
                Document doc = DocumentBuilderFactory.newInstance()
                        .newDocumentBuilder().newDocument();
                Element detail = doc.createElementNS(Soap12.SOAP_NAMESPACE, "mynamespace");
                detail.setTextContent("Missing some headers...blah");
                fault.setDetail(detail);

            } catch (ParserConfigurationException e) {
            }

            // bad: message.getInterceptorChain().abort();
            throw fault;
        }
    }
}
4

3 に答える 3

3

Donal Fellowsの提案に従って、質問に対する回答を追加します。

CXFはSpringのAOPに大きく依存しており、少なくともここでは、あらゆる種類の問題を引き起こす可能性があります。私はあなたに完全なコードを提供しています。オープンソースプロジェクトを使用して、WS-Securityを使用しないことを決定する可能性のある人に自分の数行のコードを提供するのは公正だと思います(私のサービスはSSLでのみ実行されることを期待しています)。私はCXFソースを閲覧してそのほとんどを書きました。

より良いアプローチがあると思われる場合はコメントしてください。

/**
 * Checks the requested action for AuthenticationRequired annotation and tries
 * to login using SOAP headers username/password.
 * 
 * @author Alexander Hofbauer
 */
public class AuthInterceptor extends AbstractSoapInterceptor {
    public static final String KEY_USER = "UserAuth";

    @Resource
    UserService userService;

    public AuthInterceptor() {
        // process after unmarshalling, so that method and header info are there
        super(Phase.PRE_LOGICAL);
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        Logger.getLogger(AuthInterceptor.class).trace("Intercepting service call");

        Exchange exchange = message.getExchange();
        BindingOperationInfo bop = exchange.getBindingOperationInfo();
        Method action = ((MethodDispatcher) exchange.get(Service.class)
                .get(MethodDispatcher.class.getName())).getMethod(bop);

        if (action.isAnnotationPresent(AuthenticationRequired.class)
                && !authenticate(message)) {
            Fault fault = new Fault(new Exception("Authentication failed"));
            fault.setFaultCode(new QName("Client"));

            try {
                Document doc = DocumentBuilderFactory.newInstance()
                        .newDocumentBuilder().newDocument();
                Element detail = doc.createElementNS(Soap12.SOAP_NAMESPACE, "test");
                detail.setTextContent("Failed to authenticate.\n" +
                        "Please make sure to send correct SOAP headers username and password");
                fault.setDetail(detail);

            } catch (ParserConfigurationException e) {
            }

            throw fault;
        }
    }

    private boolean authenticate(SoapMessage msg) {
        Element usernameNode = null;
        Element passwordNode = null;

        for (Header header : msg.getHeaders()) {
            if (header.getName().getLocalPart().equals("username")) {
                usernameNode = (Element) header.getObject();
            } else if (header.getName().getLocalPart().equals("password")) {
                passwordNode = (Element) header.getObject();
            }
        }

        if (usernameNode == null || passwordNode == null) {
            return false;
        }
        String username = usernameNode.getChildNodes().item(0).getNodeValue();
        String password = passwordNode.getChildNodes().item(0).getNodeValue();

        User user = null;
        try {
            user = userService.loginUser(username, password);
        } catch (BusinessException e) {
            return false;
        }
        if (user == null) {
            return false;
        }

        msg.put(KEY_USER, user);
        return true;
    }
}

上記のように、これがExceptionHandler/-Loggerです。最初は、JAX-RSと組み合わせて使用​​することはできませんでした(CXFを介しても、JAX-WSは正常に動作するようになりました)。とにかくJAX-RSは必要ないので、その問題はなくなりました。

@Aspect
public class ExceptionHandler {
    @Resource
    private Map<String, Boolean> registeredExceptions;


    /**
     * Everything in my project.
     */
    @Pointcut("within(org.myproject..*)")
    void inScope() {
    }

    /**
     * Every single method.
     */
    @Pointcut("execution(* *(..))")
    void anyOperation() {
    }

    /**
     * Log every Throwable.
     * 
     * @param t
     */
    @AfterThrowing(pointcut = "inScope() && anyOperation()", throwing = "t")
    public void afterThrowing(Throwable t) {
        StackTraceElement[] trace = t.getStackTrace();
        Logger logger = Logger.getLogger(ExceptionHandler.class);

        String info;
        if (trace.length > 0) {
            info = trace[0].getClassName() + ":" + trace[0].getLineNumber()
                    + " threw " + t.getClass().getName();
        } else {
            info = "Caught throwable with empty stack trace";
        }
        logger.warn(info + "\n" + t.getMessage());
        logger.debug("Stacktrace", t);
    }

    /**
     * Handles all exceptions according to config file.
     * Unknown exceptions are always thrown, registered exceptions only if they
     * are set to true in config file.
     * 
     * @param pjp
     * @throws Throwable
     */
    @Around("inScope() && anyOperation()")
    public Object handleThrowing(ProceedingJoinPoint pjp) throws Throwable {
        try {
            Object ret = pjp.proceed();
            return ret;
        } catch (Throwable t) {
            // We don't care about unchecked Exceptions
            if (!(t instanceof Exception)) {
                return null;
            }

            Boolean throwIt = registeredExceptions.get(t.getClass().getName());
            if (throwIt == null || throwIt) {
                throw t;
            }
        }
        return null;
    }
}
于 2011-12-11T12:18:40.657 に答える
1

簡単に言えば、リクエストを送信する前にクライアント側のインターセプターで中止する正しい方法は、ラップされた例外でフォールトを作成することです。

throw new Fault(
      new ClientException( // or any non-Fault exception, else blocks in
      // abstractClient.checkClientException() (waits for missing response code)
      "Error before sending the request"), Fault.FAULT_CODE_CLIENT);

それを理解するのを手伝ってくれた投稿者に感謝します。

于 2013-10-16T16:23:36.870 に答える
1

CXFを使用すると、インターセプターが特定のインターセプターの前または後に移動するように指定できます。インターセプターがインバウンド側で処理している場合(これは、説明に基づく場合です)、CheckFaultInterceptorと呼ばれるインターセプターがあります。インターセプターをその前に移動するように構成できます。

public HeadersInterceptor(){
    super(Phase.PRE_LOGICAL);
    getBefore().add(CheckFaultInterceptor.class.getName());
}

チェックフォールトインターセプターは、理論上、フォールトが発生したかどうかをチェックします。ある場合は、インターセプターチェーンを中止し、フォールトハンドラーチェーンを呼び出します。

私はまだこれをテストすることができませんでした(これは、関連する問題を解決しようとして出くわした利用可能なドキュメントに完全に基づいています)

于 2014-02-06T16:01:55.653 に答える