org.springframework.flex.security3.FlexAuthenticationEntryPoint.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.flex.security3.FlexAuthenticationEntryPoint.java

Source

/*
 * Copyright 2002-2011 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.flex.security3;

import java.io.IOException;
import java.util.Collections;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.flex.core.ExceptionTranslator;
import org.springframework.flex.http.AmfHttpMessageConverter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.util.CollectionUtils;

import flex.messaging.MessageException;
import flex.messaging.io.MessageIOConstants;
import flex.messaging.io.amf.ActionMessage;
import flex.messaging.io.amf.MessageBody;
import flex.messaging.messages.ErrorMessage;
import flex.messaging.messages.Message;

/**
 * An {@link AuthenticationEntryPoint} implementation to be used in conjunction with an authentication 
 * process that is completely driven by a Flex client, i.e. by presenting a Flex-based login UI and using 
 * the client-side ChannelSet API to commence authentication.  
 * 
 * <p>Mostly this class exists to satisfy the requirements of Spring Security, where it requires an 
 * <code>AuthenticationEntryPoint</code> to be provided to the {@link ExceptionTranslationFilter}.  Only in 
 * relatively exceptional cases (such as using the <code>intercept-url</code> tag to secure BlazeDS URLs, which is 
 * not recommended in preference for using Spring BlazeDS's <code>secured-endpoint-path</code> and 
 * <code>secured-channel</code> tags when using Remoting and Messaging destinations) should this implementation's 
 * {@link #commence(HttpServletRequest, HttpServletResponse, AuthenticationException)} method ever actually 
 * be invoked, as in the majority case security exceptions will never propagate out to the 
 * <code>ExceptionTranslationFilter</code>, instead being converted to a {@link MessageException} by the 
 * provided {@link ExceptionTranslator}s.  One such exceptional case might be when using RESTful Spring MVC 
 * endpoints to read and write AMF instead of the traditional RPC approach.
 * 
 * <p>When this class is used in conjunction with the XML config namespace for Flex, it will be automatically 
 * detected and its {@link #exceptionTranslators} will be configured automatically if they have not already been 
 * set explicitly as part of bean configuration.
 *
 * @author Jeremy Grelle
 */
public class FlexAuthenticationEntryPoint extends Http403ForbiddenEntryPoint {

    private static final Log log = LogFactory.getLog(FlexAuthenticationEntryPoint.class);

    private static final ExceptionTranslator DEFAULT_TRANSLATOR = new SecurityExceptionTranslator();

    private final AmfHttpMessageConverter converter = new AmfHttpMessageConverter();

    private final MediaType amfMediaType = new MediaType("application", "x-amf");

    private Set<ExceptionTranslator> exceptionTranslators;

    /**
     * If the incoming message is an {@link ActionMessage}, indicating a standard Flex Remoting or Messaging 
     * request, invokes Spring BlazeDS's {@link ExceptionTranslator}s with the {@link AuthenticationException} and 
     * sends the resulting {@link MessageException} as an AMF response to the client.
     * 
     * <p>If the request is unabled to be deserialized to AMF, if the resulting deserialized object is not an 
     * <code>ActionMessage</code>, or if no appropriate <code>ExceptionTranslator</code> is found, will simply 
     * delegate to the parent class to return a 403 response.
     */
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {

        if (CollectionUtils.isEmpty(this.exceptionTranslators)) {
            exceptionTranslators = Collections.singleton(DEFAULT_TRANSLATOR);
        }

        HttpInputMessage inputMessage = new ServletServerHttpRequest(request);
        HttpOutputMessage outputMessage = new ServletServerHttpResponse(response);

        if (!converter.canRead(Object.class, inputMessage.getHeaders().getContentType())) {
            super.commence(request, response, authException);
            return;
        }

        ActionMessage deserializedInput = null;
        try {
            deserializedInput = (ActionMessage) this.converter.read(ActionMessage.class, inputMessage);
        } catch (HttpMessageNotReadableException ex) {
            log.info("Authentication failure detected, but request could not be read as AMF.", ex);
            super.commence(request, response, authException);
            return;
        }

        if (deserializedInput instanceof ActionMessage) {
            for (ExceptionTranslator translator : this.exceptionTranslators) {
                if (translator.handles(authException.getClass())) {
                    MessageException result = translator.translate(authException);
                    ErrorMessage err = result.createErrorMessage();
                    MessageBody body = (MessageBody) ((ActionMessage) deserializedInput).getBody(0);
                    Message amfInputMessage = body.getDataAsMessage();
                    err.setCorrelationId(amfInputMessage.getMessageId());
                    err.setDestination(amfInputMessage.getDestination());
                    err.setClientId(amfInputMessage.getClientId());
                    ActionMessage responseMessage = new ActionMessage();
                    responseMessage.setVersion(((ActionMessage) deserializedInput).getVersion());
                    MessageBody responseBody = new MessageBody();
                    responseMessage.addBody(responseBody);
                    responseBody.setData(err);
                    responseBody.setTargetURI(body.getResponseURI());
                    responseBody.setReplyMethod(MessageIOConstants.STATUS_METHOD);
                    converter.write(responseMessage, amfMediaType, outputMessage);
                    response.flushBuffer();
                    return;
                }
            }
        }
        super.commence(request, response, authException);
    }

    public Set<ExceptionTranslator> getExceptionTranslators() {
        return exceptionTranslators;
    }

    public void setExceptionTranslators(Set<ExceptionTranslator> exceptionTranslators) {
        this.exceptionTranslators = exceptionTranslators;
    }
}