Frage

I have an interesting problem with me (at least it is a problem from my current point of view).

I have a RESTful web service that is exposing an interface. In our environment, we use annotation to decorate the request handlers, e.g. -

@RequiredTokenType("Binary")
public String getRecordInfo(HttpServletRequest request) {
    String recordInfo = null;

    final String methodName = "getRecordInfo()";
    _log.debug("{}: received request", methodName);

    // validate the request information
    ValidationUtils.validateAcceptHeader(request, MimeConstants.getContentType());

    .   .   .
    .   .   .

    return recordInfo;
}

We need to have a CXF interceptor/aspect (spring AOP) to execute before the handler (like the above), and check the HttpServletRequest for token type. If the token type (or any other attribute that is decorating the handler) is not of type specified in the annotation, stop the execution and return HTTP status 400 (bad request). There are about 20 request handlers like the above.

The problem I am facing here is that after writing a spring AOP based around aspect, (like below) I am able to catch the request before getRecordInfo() executes, but when I try to return 400 (or) throw an exception, the HTTP client still sees 200 -

public void validateRequest(ProceedingJoinPoint joinPoint,
                            HttpServletRequest httpRequest,
                            RequiredTokenType tokenType) throws Throwable {

    final String methodName = "validateRequest()";
    _logger.info("{}: Entered, Thread Id: {}", methodName, "" + Thread.currentThread().getId());

    // Extract the *REQUIRED* headers from the request ...
    final String tokenData = httpRequest.getHeader(HEADER_TOKEN_DATA);

    if (tokenData == null || tokenData.trim().length() < MIN_POSSIBLE_TOKEN_SIZE) {
        // Error condition .... return (400 Bad Request)
        _logger.info("{}: Invalid token. The HTTP request is rejected in lack of a valid token.");

        throw new MissingTokenException(HttpStatus.BAD_REQUEST,
                                        ErrorCode.BAD_REQUEST,
                                        "Token is missing from the request.");
    }

    ValidityToken extractedTokenFromRequest = ValidityToken.initializeFromBase64(tokenData);

    // Get the type of the token this request must include ...
    String decoratedTokenType = tokenType.value();
    _logger.debug("{}: Token Type Required: ", methodName, decoratedTokenType);

    if (! extractedTokenFromRequest.getTypeName().equals(decoratedTokenType)) {
        // Error condition .... return (400).
        _logger.info("{}: {}",
                     methodName,
                     "The token in the request mismatches the type specified in RequiredTokenType handler. 400 Bad Request.");
        throw new TokenTypeMismatchException(HttpStatus.BAD_REQUEST,
                                             ErrorCode.BAD_REQUEST,
                                             "Token type doesn't match.");
    }

    // More validations on extractedTokenFromRequest
    .   .   .
    .   .   .

    // Continue with the actual business logic if correct token is included ...
    joinPoint.proceed();
}

I have checked the log file and I can see the following log entries that confirm that both the request handler and the aspect are being called -

    getRecordInfo(): received request
    .   .   .
    .   .   .
    validateRequest(): Entered, Thread Id: 792
    .   .   .
    .   .   .
    validateRequest(): Invalid token. The HTTP request is rejected in lack of a valid token.

Despite the message, still the client see a 200 and server log shows evidence of other CXF based interceptors executing.

Here is the Spring context XML that is defining the pointcut -

<bean id="vGateKeeper" class="com....core.RequestValidator" />


<aop:config>
    <aop:aspect id="methodInvocation" ref="methodInvocationProfiler">
        <aop:around method="profileMethodInvocation"
                    pointcut="execution(* org.springframework.orm.jpa.JpaTransactionManager.commit(..))"/>
        <aop:around method="profileMethodInvocation"
                    pointcut="execution(* org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(..))"/>
    </aop:aspect>
    <aop:aspect id="gateKeeper" ref="vGateKeeper">
        <aop:pointcut expression="execution(* getRecordInfo(..)) and args(httpRequest) and @annotation(modPerms)" id="my"/>
        <aop:around pointcut-ref="my" method="validateRequest"/>
    </aop:aspect>
</aop:config>

How can Spring AOP aspect can be used to return HTTP 400 in this case, cancelling execution of any other interceptor ?

I was also looking at writing an Apache CXF interceptor to catch the call and return 400 before it reaches the request handler, but then I am not sure, How using an interceptor I can can know which request handler was suppose to execute and what annotation is decorating the request handler. Does CXF interceptor offer any way to know, which request handler will eventually execute ?

I was looking here, (https://cxf.apache.org/docs/interceptors.html) but didn't find anyway.

Any help is appreciated.

Regards,

(*Vipul)() ;

War es hilfreich?

Lösung

Actually it is rather funny that you ask the community why your advice does not work, but do not post the actual advice code. So all that is left for me to do here is guesswork. I can think of two things:

  • Your advice returns an int, but the original code returns a String. Actually an @Around advice should return the same type as the wrapped/intercepted method (or Object).
  • Your advice calls proceed(), but you do not want the original method to be executed and should not call proceed(), but return your own result.

In any case, if my two guesses are wrong, please update your question so maybe I can update my answer or someone else with CXF knowledge (which I have not, I am rather an AOP expert) can find a way for you to do it without AOP.

P.S.: One more question: Have you tested if the advice is actually fired, e.g. by writing log messages inside the advice?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top