at.gv.egovernment.moa.id.proxy.servlet.ProxyServlet.java Source code

Java tutorial

Introduction

Here is the source code for at.gv.egovernment.moa.id.proxy.servlet.ProxyServlet.java

Source

/*******************************************************************************
 * Copyright 2014 Federal Chancellery Austria
 * MOA-ID has been developed in a cooperation between BRZ, the Federal
 * Chancellery Austria - ICT staff unit, and Graz University of Technology.
 * 
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
 * the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 * http://www.osor.eu/eupl/
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 * 
 * This product combines work with different licenses. See the "NOTICE" text
 * file for details on the various modules and licenses.
 * The "NOTICE" text file is part of the distribution. Any derivative works
 * that you distribute must include a readable copy of the "NOTICE" text file.
 ******************************************************************************/
/*
 * Copyright 2003 Federal Chancellery Austria
 * MOA-ID has been developed in a cooperation between BRZ, the Federal
 * Chancellery Austria - ICT staff unit, and Graz University of Technology.
 *
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
 * the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 * http://www.osor.eu/eupl/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 *
 * This product combines work with different licenses. See the "NOTICE" text
 * file for details on the various modules and licenses.
 * The "NOTICE" text file is part of the distribution. Any derivative works
 * that you distribute must include a readable copy of the "NOTICE" text file.
 */

package at.gv.egovernment.moa.id.proxy.servlet;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import javax.net.ssl.SSLSocketFactory;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringEscapeUtils;

import at.gv.egovernment.moa.id.auth.exception.AuthenticationException;
import at.gv.egovernment.moa.id.auth.exception.BuildException;
import at.gv.egovernment.moa.id.auth.exception.MOAIDException;
import at.gv.egovernment.moa.id.auth.exception.ParseException;
import at.gv.egovernment.moa.id.auth.exception.ServiceException;
import at.gv.egovernment.moa.id.auth.servlet.RedirectServlet;
import at.gv.egovernment.moa.id.config.ConfigurationException;
import at.gv.egovernment.moa.id.config.legacy.ConnectionParameter;
import at.gv.egovernment.moa.id.config.proxy.OAConfiguration;
import at.gv.egovernment.moa.id.config.proxy.OAProxyParameter;
import at.gv.egovernment.moa.id.config.proxy.ProxyConfigurationProvider;
import at.gv.egovernment.moa.id.data.AuthenticationData;
import at.gv.egovernment.moa.id.protocols.saml1.SAML1AuthenticationData;
import at.gv.egovernment.moa.id.proxy.ConnectionBuilder;
import at.gv.egovernment.moa.id.proxy.ConnectionBuilderFactory;
import at.gv.egovernment.moa.id.proxy.LoginParameterResolver;
import at.gv.egovernment.moa.id.proxy.LoginParameterResolverException;
import at.gv.egovernment.moa.id.proxy.LoginParameterResolverFactory;
import at.gv.egovernment.moa.id.proxy.MOAIDProxyInitializer;
import at.gv.egovernment.moa.id.proxy.NotAllowedException;
import at.gv.egovernment.moa.id.proxy.invoke.GetAuthenticationDataInvoker;
import at.gv.egovernment.moa.id.util.MOAIDMessageProvider;
import at.gv.egovernment.moa.id.util.SSLUtils;
import at.gv.egovernment.moa.logging.Logger;
import at.gv.egovernment.moa.util.Base64Utils;
import at.gv.egovernment.moa.util.MiscUtil;
import at.gv.egovernment.moa.util.URLEncoder;

/**
 * Servlet requested for logging in at an online application,
 * and then for proxying requests to the online application.
 * @author Paul Ivancsics
 * @version $Id$
 */
public class ProxyServlet extends HttpServlet {
    /**
      * 
      */
    private static final long serialVersionUID = 6838184868735988125L;
    /** Name of the Parameter for the Target */
    private static final String PARAM_TARGET = "Target";
    /** Name of the Parameter for the SAMLArtifact */
    private static final String PARAM_SAMLARTIFACT = "SAMLArtifact";
    /** Name of the Parameter for the ErrorMessage */
    private static final String PARAM_ERRORMASSAGE = "error";

    /** Name of the Attribute for marking the session as authenticated*/
    private static final String ATT_AUTHDATAFETCHED = "AuthDataFetched";
    /** Name of the Attribute for the PublicURLPrefix */
    private static final String ATT_PUBLIC_URLPREFIX = "PublicURLPrefix";
    /** Name of the Attribute for the RealURLPrefix */
    private static final String ATT_REAL_URLPREFIX = "RealURLPrefix";
    /** Name of the Attribute for the SSLSocketFactory */
    private static final String ATT_SSL_SOCKET_FACTORY = "SSLSocketFactory";
    /** Name of the Attribute for the LoginHeaders */
    private static final String ATT_LOGIN_HEADERS = "LoginHeaders";
    /** Name of the Attribute for the LoginParameters */
    private static final String ATT_LOGIN_PARAMETERS = "LoginParameters";
    /** Name of the Attribute for the SAMLARTIFACT */
    private static final String ATT_SAML_ARTIFACT = "SamlArtifact";
    /** Name of the Attribute for the state of the browser request for login dialog*/
    private static final String ATT_BROWSERREQU = "BrowserLoginRequest";
    /** Name of the Attribute for the state of the browser request for login dialog*/
    private static final String ATT_OA_CONF = "oaConf";
    /** Name of the Attribute for the Logintype of the OnlineApplication*/
    private static final String ATT_OA_LOGINTYPE = "LoginType";
    /** Name of the Attribute for the number of the try to login into the OnlineApplication*/
    private static final String ATT_OA_LOGINTRY = "LoginTry";
    /** Maximum permitted login tries */
    private static final int MAX_OA_LOGINTRY = 3;
    /** Name of the Attribute for authorization value for further connections*/
    private static final String ATT_OA_AUTHORIZATION_HEADER = "authorizationkey";
    /** Name of the Attribute for user binding */
    private static final String ATT_OA_USER_BINDING = "UserBinding";
    /** For extended internal debug messages */
    private static final boolean INTERNAL_DEBUG = false;
    /** Message to be given if browser login failed */
    private static final String RET_401_MSG = "<html><head><title>Ein Fehler ist aufgetreten</title></head><body><h1>Fehler bei der Anmeldung</h1><p>Bei der Anmeldung ist ein Fehler aufgetreten.</p><p>Fehler bei der Anmeldung. <br>Pr&uuml;fen Sie bitte ihre Berechtigung.<br><b>Abbruch durch den Benutzer.</b><br></p></body></html>";

    /**
     * @see javax.servlet.http.HttpServlet#service(HttpServletRequest, HttpServletResponse)
     */
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        Logger.debug("getRequestURL:" + req.getRequestURL().toString());

        String artifact = req.getParameter(PARAM_SAMLARTIFACT);
        artifact = StringEscapeUtils.escapeHtml(artifact);

        try {
            if (artifact != null) {
                // check if SAML Artifact was already used in this session (in case of page reload)
                HttpSession session = req.getSession();
                if (null != session && artifact.equals(session.getAttribute(ATT_SAML_ARTIFACT))) {
                    if (session.getAttribute(ATT_BROWSERREQU) == null) {
                        tunnelRequest(req, resp);
                    } else {
                        login(req, resp); //login after browser login dialog
                    }
                } else
                    // it is the first time that the SAML Artifact was used
                    login(req, resp);
            } else
                tunnelRequest(req, resp);
        } catch (MOAIDException ex) {
            handleError(ex.getMessage(), ex, req, resp);
        } catch (Throwable ex) {
            handleError(ex.getMessage(), ex, req, resp);
        }
    }

    /**
     * Login to online application at first call of servlet for a user session.<br/>
     * <ul>
     * <li>Acquires authentication data from the MOA-ID Auth component.</li>
     * <li>Reads configuration data for the online application.</li>
     * <li>Resolves login parameters.</li>
     * <li>Sets up an SSLSocketFactory in case of a secure connection to the online application.</li>
     * <li>For a stateless online application, stores data in the HttpSession.</li>
     * <li>Tunnels the request to the online application.</li>
     * </ul> 
     * @param req
     * @param resp
     * @throws ConfigurationException when wrong configuration is encountered
     * @throws ProxyException when wrong configuration is encountered
     * @throws BuildException while building the request for MOA-ID Auth
     * @throws ServiceException while invoking MOA-ID Auth
     * @throws ParseException while parsing the response from MOA-ID Auth
     */
    private void login(HttpServletRequest req, HttpServletResponse resp) throws ConfigurationException,
            ProxyException, BuildException, ServiceException, ParseException, AuthenticationException {

        HttpSession session = req.getSession();
        String samlArtifact = "";
        Map loginHeaders = null;
        Map loginParameters = null;
        String publicURLPrefix = "";
        String realURLPrefix = "";
        SSLSocketFactory ssf = null;
        String urlRequested = req.getRequestURL().toString();
        OAConfiguration oaConf = null;
        String loginType = "";
        String binding = "";

        if (session.getAttribute(ATT_BROWSERREQU) == null) {

            // read configuration data
            ProxyConfigurationProvider proxyConf = ProxyConfigurationProvider.getInstance();
            OAProxyParameter oaParam = proxyConf.getOnlineApplicationParameter(urlRequested);
            if (oaParam == null) {
                throw new ProxyException("proxy.02", new Object[] { urlRequested });
            }

            samlArtifact = req.getParameter(PARAM_SAMLARTIFACT);
            Logger.debug("moa-id-proxy login " + PARAM_SAMLARTIFACT + ": " + samlArtifact);
            // String target = req.getParameter(PARAM_TARGET); parameter given but not processed
            // boolean targetprovided = req.getParameter(PARAM_TARGET) != null;

            // get authentication data from the MOA-ID Auth component
            SAML1AuthenticationData authData;
            try {
                authData = new GetAuthenticationDataInvoker().getAuthenticationData(samlArtifact);

            } catch (ServiceException ex) {
                throw new ProxyException("proxy.14", new Object[] { ex.getMessage() }, ex);

            } catch (ProxyException ex) {
                throw new ProxyException("proxy.14", new Object[] { ex.getMessage() }, ex);

            } catch (MOAIDException ex) {
                String errorURL = oaParam.getErrorRedirctURL();
                if (MiscUtil.isNotEmpty(errorURL)) {
                    generateErrorAndRedirct(resp, errorURL, ex.getMessage());
                    return;

                } else {
                    Logger.info("No ErrorRedirectURL defined. The error is shown on MOA-ID Proxy errorpage.");
                    throw new ProxyException("proxy.14", new Object[] { ex.getMessage() }, ex);
                }
            }
            session.setAttribute(ATT_AUTHDATAFETCHED, "true");

            publicURLPrefix = oaParam.getPublicURLPrefix();
            Logger.debug("OA: " + publicURLPrefix);
            oaConf = oaParam.getOaConfiguration();
            ConnectionParameter oaConnParam = oaParam.getConnectionParameter();
            realURLPrefix = oaConnParam.getUrl();

            // resolve login parameters to be forwarded to online application 
            LoginParameterResolver lpr = LoginParameterResolverFactory.getLoginParameterResolver(publicURLPrefix);
            String clientIPAddress = req.getRemoteAddr();
            boolean businessService = oaParam.getBusinessService();
            try {
                if (oaConf.getAuthType().equals(OAConfiguration.PARAM_AUTH)) {
                    loginParameters = lpr.getAuthenticationParameters(oaConf, authData, clientIPAddress,
                            businessService, publicURLPrefix);
                } else {
                    loginHeaders = lpr.getAuthenticationHeaders(oaConf, authData, clientIPAddress, businessService,
                            publicURLPrefix);
                    for (Iterator iter = loginHeaders.keySet().iterator(); iter.hasNext();) {
                        //extract user-defined bindingValue
                        String headerKey = (String) iter.next();
                        String headerKeyValue = (String) loginHeaders.get(headerKey);
                        if (headerKey.equalsIgnoreCase("binding")) {
                            binding = (String) loginHeaders.get(headerKey);
                        }
                        for (int i = 1; i <= 3; i++) {
                            if (headerKey.equalsIgnoreCase("param" + i)) {
                                int sep = headerKeyValue.indexOf("=");
                                if (sep > -1) {
                                    if (sep > 0) {
                                        String value = "";
                                        if (headerKeyValue.length() > sep + 1)
                                            value = headerKeyValue.substring(sep + 1);
                                        if (loginParameters == null)
                                            loginParameters = new HashMap();
                                        loginParameters.put(headerKeyValue.substring(0, sep), value);
                                    }
                                } else {
                                    loginParameters.put(headerKey, "");
                                }
                            }
                        }
                    }
                    loginHeaders.remove("binding");
                    loginHeaders.remove("param1");
                    loginHeaders.remove("param2");
                    loginHeaders.remove("param3");
                }
            } catch (LoginParameterResolverException ex) {
                String errorURL = oaParam.getErrorRedirctURL();
                if (MiscUtil.isNotEmpty(errorURL)) {
                    generateErrorAndRedirct(resp, errorURL, MOAIDMessageProvider.getInstance()
                            .getMessage("proxy.13", new Object[] { publicURLPrefix }));
                    return;

                } else
                    throw new ProxyException("proxy.13", new Object[] { publicURLPrefix });

            } catch (NotAllowedException e) {
                String errorURL = oaParam.getErrorRedirctURL();
                if (MiscUtil.isNotEmpty(errorURL)) {
                    generateErrorAndRedirct(resp, errorURL,
                            MOAIDMessageProvider.getInstance().getMessage("proxy.15", new Object[] {}));
                    return;

                } else
                    throw new ProxyException("proxy.15", new Object[] {});
            }

            // setup SSLSocketFactory for communication with the online application
            if (oaConnParam.isHTTPSURL()) {
                try {
                    ssf = SSLUtils.getSSLSocketFactory(proxyConf, oaConnParam);
                } catch (Throwable ex) {
                    throw new ProxyException("proxy.05", new Object[] { oaConnParam.getUrl(), ex.toString() }, ex);
                }
            }

            // for stateless online application, store data in HttpSession
            loginType = oaConf.getLoginType();
            if ("".equalsIgnoreCase(binding)) {
                binding = oaConf.getBinding();
                if ("".equalsIgnoreCase(binding))
                    binding = "full";
            }
            Logger.debug("Login type: " + loginType);
            if (loginType.equals(OAConfiguration.LOGINTYPE_STATELESS)) {
                int sessionTimeOut = oaParam.getSessionTimeOut();
                if (sessionTimeOut == 0)
                    sessionTimeOut = 60 * 60; // default 1 h

                session.setMaxInactiveInterval(sessionTimeOut);
                session.setAttribute(ATT_PUBLIC_URLPREFIX, publicURLPrefix);
                session.setAttribute(ATT_REAL_URLPREFIX, realURLPrefix);
                session.setAttribute(ATT_SSL_SOCKET_FACTORY, ssf);
                session.setAttribute(ATT_LOGIN_HEADERS, loginHeaders);
                session.setAttribute(ATT_LOGIN_PARAMETERS, loginParameters);
                session.setAttribute(ATT_SAML_ARTIFACT, samlArtifact);
                session.setAttribute(ATT_OA_CONF, oaConf);
                session.setAttribute(ATT_OA_LOGINTYPE, loginType);
                session.setAttribute(ATT_OA_USER_BINDING, binding);
                session.removeAttribute(ATT_BROWSERREQU);
                session.removeAttribute(ATT_OA_AUTHORIZATION_HEADER);
                session.removeAttribute(ATT_OA_LOGINTRY);
                Logger.debug("moa-id-proxy: HTTPSession " + session.getId() + " angelegt");
            }

        } else {
            loginHeaders = (Map) session.getAttribute(ATT_LOGIN_HEADERS);
            publicURLPrefix = (String) session.getAttribute(ATT_PUBLIC_URLPREFIX);
            realURLPrefix = (String) session.getAttribute(ATT_REAL_URLPREFIX);
            ssf = (SSLSocketFactory) session.getAttribute(ATT_SSL_SOCKET_FACTORY);
            loginHeaders = (Map) session.getAttribute(ATT_LOGIN_HEADERS);
            loginParameters = (Map) session.getAttribute(ATT_LOGIN_PARAMETERS);
            samlArtifact = (String) session.getAttribute(ATT_SAML_ARTIFACT);
            oaConf = (OAConfiguration) session.getAttribute(ATT_OA_CONF);
            loginType = (String) session.getAttribute(ATT_OA_LOGINTYPE);
            binding = (String) session.getAttribute(ATT_OA_USER_BINDING);
            session.removeAttribute(ATT_BROWSERREQU);
            Logger.debug("moa-id-proxy: HTTPSession " + session.getId() + " aufgenommen");
        }

        try {
            int respcode = 0;

            // tunnel request to the online application
            respcode = tunnelRequest(req, resp, loginHeaders, loginParameters, publicURLPrefix, realURLPrefix, ssf,
                    binding);
            if (respcode == 401) {
                if (OAConfiguration.BINDUNG_FULL.equals(binding)
                        && oaConf.getLoginType().equals(OAConfiguration.LOGINTYPE_STATELESS)) {
                    throw new ProxyException("proxy.12", new Object[] { realURLPrefix });
                }
            }
        } catch (ProxyException ex) {
            throw new ProxyException("proxy.12", new Object[] { realURLPrefix });
        } catch (Throwable ex) {
            throw new ProxyException("proxy.04", new Object[] { urlRequested, ex.toString() }, ex);
        }
    }

    /**
     * Tunnels a request to the stateless online application using data stored in the HTTP session.
     * @param req HTTP request
     * @param resp HTTP response
     * @throws IOException if an I/O error occurs
     */
    private void tunnelRequest(HttpServletRequest req, HttpServletResponse resp)
            throws ProxyException, IOException {

        //Logger.debug("Tunnel request (stateless)");
        HttpSession session = req.getSession(false);

        if (session == null)
            throw new ProxyException("proxy.07", null);
        String publicURLPrefix = (String) session.getAttribute(ATT_PUBLIC_URLPREFIX);
        //A session is automatically created when forwarded 1st time to errorpage-proxy.jsp (with the handleError method)
        //additional check if publicURLPrefix is OK, if not throw an Exception
        if (publicURLPrefix == null)
            throw new ProxyException("proxy.07", null);

        String realURLPrefix = (String) session.getAttribute(ATT_REAL_URLPREFIX);
        SSLSocketFactory ssf = (SSLSocketFactory) session.getAttribute(ATT_SSL_SOCKET_FACTORY);
        Map loginHeaders = (Map) session.getAttribute(ATT_LOGIN_HEADERS);
        Map loginParameters = (Map) session.getAttribute(ATT_LOGIN_PARAMETERS);
        String binding = (String) session.getAttribute(ATT_OA_USER_BINDING);
        if (publicURLPrefix == null || realURLPrefix == null)
            throw new ProxyException("proxy.08", new Object[] { req.getRequestURL().toString() });

        int respcode = tunnelRequest(req, resp, loginHeaders, loginParameters, publicURLPrefix, realURLPrefix, ssf,
                binding);
        if (respcode == -401) // #tries to login exceeded
            throw new ProxyException("proxy.16", new Object[] { realURLPrefix, Integer.toString(MAX_OA_LOGINTRY) });
    }

    /**
     * Tunnels a request to the online application using given URL mapping and SSLSocketFactory.
     * This method returns the ResponseCode of the request to the online application. 
     * @param req HTTP request
     * @param resp HTTP response
     * @param loginHeaders header field/values to be inserted for purposes of authentication; 
     *         may be <code>null</code>
     * @param loginParameters parameter name/values to be inserted for purposes of authentication; 
     *         may be <code>null</code>
     * @param publicURLPrefix prefix of request URL to be substituted for the <code>realURLPrefix</code>
     * @param realURLPrefix prefix of online application URL to substitute the <code>publicURLPrefix</code>
     * @param ssf SSLSocketFactory to use
     * @throws IOException if an I/O error occurs
     */
    private int tunnelRequest(HttpServletRequest req, HttpServletResponse resp, Map loginHeaders,
            Map loginParameters, String publicURLPrefix, String realURLPrefix, SSLSocketFactory ssf, String binding)
            throws IOException {

        String originBinding = binding;
        String browserUserID = "";
        String browserPassword = "";
        //URL url = new URL(realURLPrefix); 
        //String realURLHost = url.getHost(); 
        if (INTERNAL_DEBUG && !binding.equals(""))
            Logger.debug("Binding: " + binding);

        // collect headers from request
        Map headers = new HashMap();
        for (Enumeration enu = req.getHeaderNames(); enu.hasMoreElements();) {
            String headerKey = (String) enu.nextElement();
            String headerKeyValue = req.getHeader(headerKey);
            if (INTERNAL_DEBUG)
                Logger.debug("Incoming:" + headerKey + "=" + headerKeyValue);
            //Analyze Basic-Auth-Headers from the client
            if (headerKey.equalsIgnoreCase("Authorization")) {
                if (headerKeyValue.substring(0, 6).equalsIgnoreCase("Basic ")) {
                    String credentials = headerKeyValue.substring(6);
                    byte[] bplaintextcredentials = Base64Utils.decode(credentials, true);
                    String plaintextcredentials = new String(bplaintextcredentials);
                    browserUserID = plaintextcredentials.substring(0, plaintextcredentials.indexOf(":"));
                    browserPassword = plaintextcredentials.substring(plaintextcredentials.indexOf(":") + 1);
                    //deactivate following line for security
                    //if (INTERNAL_DEBUG) Logger.debug("Analyzing authorization-header from browser: " + headerKeyValue + "gives UN:PW=" + browserUserID + ":" + browserPassword );
                }
                if (headerKeyValue.substring(0, 9).equalsIgnoreCase("Negotiate")) {
                    //deactivate following line for security
                    //if (INTERNAL_DEBUG) Logger.debug("Analyzing authorization-header from browser: Found NTLM Aut.: " + headerKeyValue + "gives UN:PW=" + browserUserID + ":" + browserPassword );
                }
            } else {
                /* Headers MUST NOT be repaced according to our Spec.
                if (headerKey.equalsIgnoreCase("Host")) {
                   headerKeyValue = realURLHost; 
                     //headerKeyValue= realURLPrefix.substring(hoststartpos);
                  if (INTERNAL_DEBUG) Logger.debug("replaced:" + headerKey + "=" + headerKeyValue);           
                }
                */
                headers.put(headerKey, headerKeyValue);
            }
        }

        // collect login headers, possibly overwriting headers from request
        String authorizationvalue = "";
        if (req.getSession().getAttribute(ATT_OA_AUTHORIZATION_HEADER) == null) {

            if (OAConfiguration.BINDUNG_NOMATCH.equals(binding)) {
                int loginTry = getLoginTry(req);
                Logger.debug("Binding: mode = " + OAConfiguration.BINDUNG_NOMATCH + "(try #"
                        + Integer.toString(loginTry) + ")");
                if (loginTry == 1) {
                    binding = OAConfiguration.BINDUNG_FULL;
                } else {
                    binding = OAConfiguration.BINDUNG_USERNAME;
                }
            }

            /* Soll auch bei anderen bindings zuerst ein passwort probiert werden knnen:
            //if we have the first Login-Try and we have Binding to Username and a predefined Password we try this one first
             // full binding will be covered by next block
             if (loginTry==1 && !OAConfiguration.BINDUNG_FULL.equals(binding)) {
               //1st try: if we have a password, try this one first
                for (Iterator iter = loginHeaders.keySet().iterator(); iter.hasNext();) {
                  String headerKey = (String) iter.next();
                  String headerKeyValue = (String) loginHeaders.get(headerKey);
                  if (isBasicAuthenticationHeader(headerKey, headerKeyValue)) {
                   String credentials = headerKeyValue.substring(6);
                   byte [] bplaintextcredentials = Base64Utils.decode(credentials, true);
                  String plaintextcredentials = new String(bplaintextcredentials);
                  String password = plaintextcredentials.substring(plaintextcredentials.indexOf(":")+1);
                  if (password!=null && !password.equals("")) {
                      Logger.debug("Binding: found predefined password. Trying full binding first");
                     binding = OAConfiguration.BINDUNG_FULL;
                     break;
                    }
                  }
                }
             }
             */

            //we have a connection with not having logged on
            if (loginHeaders != null && (browserPassword.length() != 0 || browserUserID.length() != 0
                    || OAConfiguration.BINDUNG_FULL.equals(binding))) {
                for (Iterator iter = loginHeaders.keySet().iterator(); iter.hasNext();) {
                    String headerKey = (String) iter.next();
                    String headerKeyValue = (String) loginHeaders.get(headerKey);
                    //customize loginheaders if necessary
                    if (isBasicAuthenticationHeader(headerKey, headerKeyValue)) {
                        if (OAConfiguration.BINDUNG_FULL.equals(binding)) {
                            authorizationvalue = headerKeyValue;
                            Logger.debug("Binding: full binding to user established");
                        } else {
                            String credentials = headerKeyValue.substring(6);
                            byte[] bplaintextcredentials = Base64Utils.decode(credentials, true);
                            String plaintextcredentials = new String(bplaintextcredentials);
                            String userID = plaintextcredentials.substring(0, plaintextcredentials.indexOf(":"));
                            String password = plaintextcredentials.substring(plaintextcredentials.indexOf(":") + 1);
                            String userIDPassword = ":";
                            if (OAConfiguration.BINDUNG_USERNAME.equals(binding)) {
                                Logger.debug("Binding: Access with necessary binding to user");
                                userIDPassword = userID + ":" + browserPassword;
                            } else if (OAConfiguration.BINDUNG_NONE.equals(binding)) {
                                Logger.debug("Binding: Access without binding to user");
                                //If first time
                                if (browserUserID.length() == 0)
                                    browserUserID = userID;
                                if (browserPassword.length() == 0)
                                    browserPassword = password;
                                userIDPassword = browserUserID + ":" + browserPassword;
                            } else {
                                userIDPassword = userID + ":" + password;
                            }
                            credentials = Base64Utils.encode(userIDPassword.getBytes());
                            authorizationvalue = "Basic " + credentials;
                            headerKeyValue = authorizationvalue;
                        }
                    }
                    headers.put(headerKey, headerKeyValue);
                }
            }
        } else {
            //if OA needs Authorization header in each further request
            authorizationvalue = (String) req.getSession().getAttribute(ATT_OA_AUTHORIZATION_HEADER);
            if (loginHeaders != null)
                headers.put("Authorization", authorizationvalue);
        }

        Vector parameters = new Vector();
        for (Enumeration enu = req.getParameterNames(); enu.hasMoreElements();) {
            String paramName = (String) enu.nextElement();
            if (!(paramName.equals(PARAM_SAMLARTIFACT) || paramName.equals(PARAM_TARGET))) {
                if (INTERNAL_DEBUG)
                    Logger.debug("Req Parameter-put: " + paramName + ":" + req.getParameter(paramName));
                String parameter[] = new String[2];
                parameter[0] = paramName;
                parameter[1] = req.getParameter(paramName);
                parameters.add(parameter);
            }
        }
        // collect login parameters, possibly overwriting parameters from request
        if (loginParameters != null) {
            for (Iterator iter = loginParameters.keySet().iterator(); iter.hasNext();) {
                String paramName = (String) iter.next();
                if (!(paramName.equals(PARAM_SAMLARTIFACT) || paramName.equals(PARAM_TARGET))) {
                    if (INTERNAL_DEBUG)
                        Logger.debug(
                                "Req Login-Parameter-put: " + paramName + ":" + loginParameters.get(paramName));
                    String parameter[] = new String[2];
                    parameter[0] = paramName;
                    parameter[1] = (String) loginParameters.get(paramName);
                    parameters.add(parameter);
                }
            }
        }

        ConnectionBuilder cb = ConnectionBuilderFactory.getConnectionBuilder(publicURLPrefix);
        HttpURLConnection conn = cb.buildConnection(req, publicURLPrefix, realURLPrefix, ssf, parameters);

        // set headers as request properties of URLConnection
        for (Iterator iter = headers.keySet().iterator(); iter.hasNext();) {
            String headerKey = (String) iter.next();
            String headerValue = (String) headers.get(headerKey);
            String LogStr = "Req header " + headerKey + ": " + headers.get(headerKey);
            if (isBasicAuthenticationHeader(headerKey, headerValue)) {
                String credentials = headerValue.substring(6);
                byte[] bplaintextcredentials = Base64Utils.decode(credentials, true);
                String plaintextcredentials = new String(bplaintextcredentials);
                String uid = plaintextcredentials.substring(0, plaintextcredentials.indexOf(":"));
                String pwd = plaintextcredentials.substring(plaintextcredentials.indexOf(":") + 1);
                //Sollte AuthorizationInfo vom HTTPClient benutzt werden:  cb.addBasicAuthorization(publicURLPrefix, uid, pwd);
                //deactivate following line for security
                //if (INTERNAL_DEBUG && Logger.isDebugEnabled()) LogStr = LogStr + "  >UserID:Password< >" + uid + ":" + pwd + "<";
            }
            conn.setRequestProperty(headerKey, headerValue);
            if (INTERNAL_DEBUG)
                Logger.debug(LogStr);
        }

        StringWriter sb = new StringWriter();

        // Write out parameters into output stream of URLConnection.
        // On GET request, do not send parameters in any case,
        // otherwise HttpURLConnection would send a POST.
        if (!"get".equalsIgnoreCase(req.getMethod()) && !parameters.isEmpty()) {
            boolean firstParam = true;
            String parameter[] = new String[2];
            for (Iterator iter = parameters.iterator(); iter.hasNext();) {
                parameter = (String[]) iter.next();
                String paramName = parameter[0];
                String paramValue = parameter[1];
                if (firstParam)
                    firstParam = false;
                else
                    sb.write("&");
                sb.write(paramName);
                sb.write("=");
                sb.write(paramValue);
                if (INTERNAL_DEBUG)
                    Logger.debug("Req param " + paramName + ": " + paramValue);
            }
        }

        // For WebDAV and POST: copy content
        if (!"get".equalsIgnoreCase(req.getMethod())) {
            if (INTERNAL_DEBUG && !"post".equalsIgnoreCase(req.getMethod()))
                Logger.debug("---- WEBDAV ----  copying content");
            try {
                OutputStream out = conn.getOutputStream();
                InputStream in = req.getInputStream();
                if (!parameters.isEmpty())
                    out.write(sb.toString().getBytes()); //Parameter nicht mehr mittels Printwriter schreiben 
                copyStream(in, out, null, req.getMethod());
                out.flush();
                out.close();
            } catch (IOException e) {
                if (!"post".equalsIgnoreCase(req.getMethod()))
                    Logger.debug("---- WEBDAV ----  streamcopy problem");
                else
                    Logger.debug("---- POST ----  streamcopy problem");
            }
        }

        // connect
        if (INTERNAL_DEBUG)
            Logger.debug("Connect Request");
        conn.connect();
        if (INTERNAL_DEBUG)
            Logger.debug("Connect Response");

        // check login tries
        if (conn.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
            int loginTry = getLoginTry(req);
            req.getSession().setAttribute(ATT_OA_LOGINTRY, Integer.toString(loginTry));
            if (loginTry > MAX_OA_LOGINTRY) {
                Logger.debug("Found 401 UNAUTHORIZED, maximum tries exceeded; leaving...");
                cb.disconnect(conn);
                return -401;
            }
        }

        if (conn.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED
                && OAConfiguration.BINDUNG_FULL.equals(originBinding)) {
            Logger.debug("Found 401 UNAUTHORIZED, leaving...");
            cb.disconnect(conn);
            return conn.getResponseCode();
        }

        resp.setStatus(conn.getResponseCode());
        //Issue by Gregor Karlinger - content type was annotated twice
        //resp.setContentType(conn.getContentType());

        if (loginHeaders != null
                && (conn.getResponseCode() == HttpURLConnection.HTTP_OK
                        || conn.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP)
                && req.getSession().getAttribute(ATT_OA_AUTHORIZATION_HEADER) == null) {
            req.getSession().setAttribute(ATT_OA_AUTHORIZATION_HEADER, authorizationvalue);
            Logger.debug("Login OK. Saving authorization header to remember in further requests");
        }

        // Read response headers
        // Omit response header "content-length" if response header "Transfer-encoding: chunked" is set.
        // Otherwise, the connection will not be kept alive, resulting in subsequent missing requests.
        // See JavaDoc of javax.servlet.http.HttpServlet:
        // When using HTTP 1.1 chunked encoding (which means that the response has a Transfer-Encoding header), do not set the Content-Length header.
        Vector respHeaders = new Vector();

        boolean chunked = false;
        String contentLengthKey = null;
        String transferEncodingKey = null;
        int i = 1;
        String headerKey;
        String loginType = (String) req.getSession().getAttribute(ATT_OA_LOGINTYPE);
        while ((headerKey = conn.getHeaderFieldKey(i)) != null) {
            String headerValue = conn.getHeaderField(i);

            if (headerKey.equalsIgnoreCase("WWW-Authenticate")) {
                int start = headerValue.indexOf("Basic realm=\"");
                boolean requestsBasicAuth = headerValue.substring(start).startsWith("Basic realm=\"");
                if (requestsBasicAuth) {
                    headerValue = "Basic realm=\"" + publicURLPrefix + "\"";

                    if (OAConfiguration.BINDUNG_USERNAME.equals(originBinding)
                            || OAConfiguration.BINDUNG_NOMATCH.equals(originBinding))
                        headerValue = "Basic realm=\"Bitte Passwort eingeben\"";
                    else if ("none".equals(originBinding)) {
                        headerValue = "Basic realm=\"Bitte Benutzername und Passwort eingeben\"";
                    }
                }
            }

            //    // berschrift im Browser-Passworteingabedialog setzen (sonst ist der reale host eingetragen)
            //    if (headerKey.equalsIgnoreCase("WWW-Authenticate") && headerValue.startsWith("Basic realm=\"")) {
            //      headerValue = "Basic realm=\"" + publicURLPrefix + "\"";
            //      if (OAConfiguration.BINDUNG_USERNAME.equals(originBinding) || OAConfiguration.BINDUNG_NOMATCH.equals(originBinding)) {
            //         headerValue = "Basic realm=\"Bitte Passwort eingeben\"";
            //      } else if (OAConfiguration.BINDUNG_NONE.equals(originBinding)) {
            //         headerValue = "Basic realm=\"Bitte Benutzername und Passwort eingeben\"";
            //      }
            //    }

            String respHeader[] = new String[2];
            if ((conn.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
                    && headerKey.equalsIgnoreCase("content-length")) {
                //alter the unauthorized message with template for login 
                //TODO: supply a special login form on unauthorized messages with bindings!=full
                headerValue = Integer.toString(RET_401_MSG.length());
            }
            respHeader[0] = headerKey;
            respHeader[1] = headerValue;

            if (!(OAConfiguration.BINDUNG_FULL.equals(originBinding)
                    && OAConfiguration.LOGINTYPE_STATELESS.equals(loginType)
                    && headerKey.equalsIgnoreCase("WWW-Authenticate")
                    && headerValue.startsWith("Basic realm=\""))) {
                respHeaders.add(respHeader);
                if (INTERNAL_DEBUG)
                    Logger.debug("Resp header " + headerKey + ": " + headerValue);
            } else {
                Logger.debug("Resp header ---REMOVED--- " + headerKey + ": " + headerValue);
            }
            if (isTransferEncodingChunkedHeader(headerKey, headerValue)
                    || "content-length".equalsIgnoreCase(headerKey)) {
                respHeaders.remove(respHeader);
                Logger.debug("Resp header " + headerKey + " REMOVED");
            }

            i++;
        }

        String headerValue;
        String respHeader[] = new String[2];

        //write out all Responseheaders 
        for (Iterator iter = respHeaders.iterator(); iter.hasNext();) {
            respHeader = (String[]) iter.next();
            headerKey = respHeader[0];
            headerValue = respHeader[1];
            resp.addHeader(headerKey, headerValue);
        }

        //Logger.debug(">>>> Copy Content");
        //Logger.debug("  from ()" + conn.getURL());
        //Logger.debug("  to (" + req.getRemoteAddr() + ":"+ ") " +req.getRequestURL());

        // read response stream
        Logger.debug("Resp from " + conn.getURL().toString() + ": status " + conn.getResponseCode());
        // Load content unless the server lets us know that the content is NOT MODIFIED...
        if (conn.getResponseCode() != HttpURLConnection.HTTP_NOT_MODIFIED) {
            BufferedInputStream respIn = new BufferedInputStream(conn.getInputStream());
            //Logger.debug("Got Inputstream");
            BufferedOutputStream respOut = new BufferedOutputStream(resp.getOutputStream());
            //Logger.debug("Got Outputstream");

            byte[] buffer = new byte[4096];
            if (respOut != null) {
                int bytesRead;
                while ((bytesRead = respIn.read(buffer)) >= 0) {
                    if (conn.getResponseCode() != HttpURLConnection.HTTP_UNAUTHORIZED)
                        respOut.write(buffer, 0, bytesRead);
                }
            } else {
                while (respIn.read(buffer) >= 0)
                    ;
            }

            /*
            int ch;
            StringBuffer strBuf = new StringBuffer("");
            while ((ch = respIn.read()) >= 0) {
              if (conn.getResponseCode()!=HttpURLConnection.HTTP_UNAUTHORIZED) respOut.write(ch);
              strBuf.append((char)ch);
            }
            Logger.debug("Resp Content:");
            if (strBuf.toString().length()>500)
              Logger.debug(strBuf.toString().substring(0,500));
            else
              Logger.debug(strBuf.toString());
            */

            if (conn.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
                respOut.write(RET_401_MSG.getBytes());
            }
            respOut.flush();
            respOut.close();
            respIn.close();
            if (conn.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
                Logger.debug("Found 401 UNAUTHORIZED...");
                cb.disconnect(conn);
                return conn.getResponseCode();
            }
        } else {
            //if (conn.getResponseCode()==HttpURLConnection.HTTP_NOT_MODIFIED) 
            Logger.debug("Found 304 NOT MODIFIED...");
        }

        cb.disconnect(conn);
        Logger.debug("Request done");

        return conn.getResponseCode();
    }

    /**
     * Gets the current amount of the login try at the online application
     * 
     * @param req the HttpServletRequest
     * @return the number off the current login try
     */
    private int getLoginTry(HttpServletRequest req) {
        String oa_loginTry = (String) req.getSession().getAttribute(ATT_OA_LOGINTRY);
        int loginTry = 1;
        if (oa_loginTry != null)
            loginTry = Integer.parseInt(oa_loginTry) + 1;
        return loginTry;
    }

    /**
     * Determines whether a HTTP header is a basic authentication header of the kind "Authorization: Basic ..."
     * 
     * @param headerKey header name
     * @param headerValue header value
     * @return true for a basic authentication header
     */
    private boolean isBasicAuthenticationHeader(String headerKey, String headerValue) {
        if (!"authorization".equalsIgnoreCase(headerKey))
            return false;
        if (headerValue.length() < "basic".length())
            return false;
        String authenticationSchema = headerValue.substring(0, "basic".length());
        return "basic".equalsIgnoreCase(authenticationSchema);
    }

    /**
     * Determines whether a basic authentication header of the kind "Authorization: Basic ..."
     * is included in a HTTP request
     * @param req HTTP request
     * @return true for a basic authentication header provided
     */
    private boolean isBasicAuthenticationHeaderProvided(HttpServletRequest req) {
        for (Enumeration enu = req.getHeaderNames(); enu.hasMoreElements();) {
            String headerKey = (String) enu.nextElement();
            String headerValue = req.getHeader(headerKey);
            if (isBasicAuthenticationHeader(headerKey, headerValue))
                return true;
        }
        return false;
    }

    /**
     * Determines whether a HTTP header is "Transfer-encoding" header with value containing "chunked" 
     *  
     * @param headerKey header name
     * @param headerValue header value
     * @return true for a "Transfer-encoding: chunked" header
     */
    private boolean isTransferEncodingChunkedHeader(String headerKey, String headerValue) {
        if (!"transfer-encoding".equalsIgnoreCase(headerKey))
            return false;
        return headerValue.indexOf("chunked") >= 0 || headerValue.indexOf("Chunked") >= 0
                || headerValue.indexOf("CHUNKED") >= 0;
    }

    /**
     * Calls the web application initializer.
     * 
     * @see javax.servlet.Servlet#init(ServletConfig)
     */
    public void init(ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);
        try {
            MOAIDProxyInitializer.initialize();
            Logger.info(MOAIDMessageProvider.getInstance().getMessage("proxy.00", null));
        } catch (Exception ex) {
            Logger.fatal(MOAIDMessageProvider.getInstance().getMessage("proxy.06", null), ex);
            throw new ServletException(ex);
        }
    }

    /**
     * Handles an error. <br>
     * <ul>
     * <li>Logs the error</li>
     * <li>Places error message and exception thrown into the request 
     *        as request attributes (to be used by <code>"/errorpage-proxy.jsp"</code>)</li>
     * <li>Sets HTTP status 500 (internal server error)</li>
     * </ul>
     * 
     * @param errorMessage error message
     * @param exceptionThrown exception thrown
     * @param req servlet request
     * @param resp servlet response
     */
    protected void handleError(String errorMessage, Throwable exceptionThrown, HttpServletRequest req,
            HttpServletResponse resp) {

        if (null != errorMessage) {
            Logger.error(errorMessage);
            req.setAttribute("ErrorMessage", errorMessage);
        }

        if (null != exceptionThrown) {
            if (null == errorMessage)
                errorMessage = exceptionThrown.getMessage();
            Logger.error(errorMessage, exceptionThrown);
            //req.setAttribute("ExceptionThrown", exceptionThrown);
        }

        if (Logger.isDebugEnabled()) {
            req.setAttribute("LogLevel", "debug");
        }

        //forward this to errorpage-proxy.jsp where the HTML error page is generated
        ServletContext context = getServletContext();
        RequestDispatcher dispatcher = context.getRequestDispatcher("/errorpage-proxy.jsp");
        try {
            dispatcher.forward(req, resp);
        } catch (ServletException e) {
            Logger.error(e);
        } catch (IOException e) {
            Logger.error(e);
        }

    }

    // * taken from iaik.utils.util.copyStream:
    /**
     * Reads all data (until EOF is reached) from the given source to the 
     * destination stream. If the destination stream is null, all data is dropped.
     * It uses the given buffer to read data and forward it. If the buffer is 
     * null, this method allocates a buffer.
     *
     * @param source The stream providing the data.
     * @param destination The stream that takes the data. If this is null, all
     *                    data from source will be read and discarded.
     * @param buffer The buffer to use for forwarding. If it is null, the method
     *               allocates a buffer.
     * @exception IOException If reading from the source or writing to the 
     *                        destination fails.
     */
    private static void copyStream(InputStream source, OutputStream destination, byte[] buffer, String method)
            throws IOException {
        if (source == null) {
            throw new NullPointerException("Argument \"source\" must not be null.");
        }
        if (buffer == null) {
            buffer = new byte[4096];
        }

        if (destination != null) {
            int bytesRead;
            while ((bytesRead = source.read(buffer)) >= 0) {
                destination.write(buffer, 0, bytesRead);
                //if (method.equalsIgnoreCase("POST")) Logger.debug(buffer.toString());
            }
        } else {
            while (source.read(buffer) >= 0)
                ;
        }
    }

    private static void generateErrorAndRedirct(HttpServletResponse resp, String errorURL, String message) {
        try {
            errorURL = addURLParameter(errorURL, PARAM_ERRORMASSAGE, URLEncoder.encode(message, "UTF-8"));

        } catch (UnsupportedEncodingException e) {
            errorURL = addURLParameter(errorURL, PARAM_ERRORMASSAGE,
                    "Fehlermeldung%20konnte%20nicht%20%C3%BCbertragen%20werden.");
        }

        errorURL = resp.encodeRedirectURL(errorURL);
        resp.setContentType("text/html");
        resp.setStatus(302);
        resp.addHeader("Location", errorURL);
    }

    protected static String addURLParameter(String url, String paramname, String paramvalue) {
        String param = paramname + "=" + paramvalue;
        if (url.indexOf("?") < 0)
            return url + "?" + param;
        else
            return url + "&" + param;
    }

}