217 lines · 7.7 KB · Java
RawDownload
/* * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.net.www.protocol.http; import java.net.URL;import java.io.IOException;import java.net.Authenticator.RequestorType;import java.util.HashMap;import sun.net.www.HeaderParser;import sun.misc.BASE64Decoder;import sun.misc.BASE64Encoder;import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;import static sun.net.www.protocol.http.AuthScheme.KERBEROS; /** * NegotiateAuthentication: * * @author weijun.wang@sun.com * @since 1.6 */ class NegotiateAuthentication extends AuthenticationInfo {     private static final long serialVersionUID = 100L;     final private HttpCallerInfo hci;     // These maps are used to manage the GSS availability for diffrent    // hosts. The key for both maps is the host name.    // <code>supported</code> is set when isSupported is checked,    // if it's true, a cached Negotiator is put into <code>cache</code>.    // the cache can be used only once, so after the first use, it's cleaned.    static HashMap <String, Boolean> supported = null;    static HashMap <String, Negotiator> cache = null;     // The HTTP Negotiate Helper    private Negotiator negotiator = null;    /**    * Constructor used for both WWW and proxy entries.    * @param hci a schemed object.    */    public NegotiateAuthentication(HttpCallerInfo hci) {        super(RequestorType.PROXY==hci.authType ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,              hci.scheme.equalsIgnoreCase("Negotiate") ? NEGOTIATE : KERBEROS,              hci.url,              "");        this.hci = hci;    }     /**     * @return true if this authentication supports preemptive authorization     */    @Override    public boolean supportsPreemptiveAuthorization() {        return false;    }     /**     * Find out if the HttpCallerInfo supports Negotiate protocol. In order to     * find out yes or no, an initialization of a Negotiator object against it     * is tried. The generated object will be cached under the name of ths     * hostname at a success try.<br>     *     * If this method is called for the second time on an HttpCallerInfo with     * the same hostname, the answer is retrieved from cache.     *     * @return true if supported     */    synchronized public static boolean isSupported(HttpCallerInfo hci) {        if (supported == null) {            supported = new HashMap <String, Boolean>();            cache = new HashMap <String, Negotiator>();        }        String hostname = hci.host;        hostname = hostname.toLowerCase();        if (supported.containsKey(hostname)) {            return supported.get(hostname);        }         Negotiator neg = Negotiator.getNegotiator(hci);        if (neg != null) {            supported.put(hostname, true);            // the only place cache.put is called. here we can make sure            // the object is valid and the oneToken inside is not null            cache.put(hostname, neg);            return true;        } else {            supported.put(hostname, false);            return false;        }    }     /**     * Not supported. Must use the setHeaders() method     */    @Override    public String getHeaderValue(URL url, String method) {        throw new RuntimeException ("getHeaderValue not supported");    }     /**     * Check if the header indicates that the current auth. parameters are stale.     * If so, then replace the relevant field with the new value     * and return true. Otherwise return false.     * returning true means the request can be retried with the same userid/password     * returning false means we have to go back to the user to ask for a new     * username password.     */    @Override    public boolean isAuthorizationStale (String header) {        return false; /* should not be called for Negotiate */    }     /**     * Set header(s) on the given connection.     * @param conn The connection to apply the header(s) to     * @param p A source of header values for this connection, not used because     *          HeaderParser converts the fields to lower case, use raw instead     * @param raw The raw header field.     * @return true if all goes well, false if no headers were set.     */    @Override    public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {         try {            String response;            byte[] incoming = null;            String[] parts = raw.split("\\s+");            if (parts.length > 1) {                incoming = new BASE64Decoder().decodeBuffer(parts[1]);            }            response = hci.scheme + " " + new B64Encoder().encode(                        incoming==null?firstToken():nextToken(incoming));             conn.setAuthenticationProperty(getHeaderName(), response);            return true;        } catch (IOException e) {            return false;        }    }     /**     * return the first token.     * @returns the token     * @throws IOException if <code>Negotiator.getNegotiator()</code> or     *                     <code>Negotiator.firstToken()</code> failed.     */    private byte[] firstToken() throws IOException {        negotiator = null;        if (cache != null) {            synchronized(cache) {                negotiator = cache.get(getHost());                if (negotiator != null) {                    cache.remove(getHost()); // so that it is only used once                }            }        }        if (negotiator == null) {            negotiator = Negotiator.getNegotiator(hci);            if (negotiator == null) {                IOException ioe = new IOException("Cannot initialize Negotiator");                throw ioe;            }        }         return negotiator.firstToken();    }     /**     * return more tokens     * @param token the token to be fed into <code>negotiator.nextToken()</code>     * @returns the token     * @throws IOException if <code>negotiator.nextToken()</code> throws Exception.     *  May happen if the input token is invalid.     */    private byte[] nextToken(byte[] token) throws IOException {        return negotiator.nextToken(token);    }     class B64Encoder extends BASE64Encoder {        protected int bytesPerLine () {            return 100000;  // as big as it can be, maybe INT_MAX        }    }     // MS will send a final WWW-Authenticate even if the status is already    // 200 OK. The token can be fed into initSecContext() again to determine    // if the server can be trusted. This is not the same concept as Digest's    // Authentication-Info header.    //    // Currently we ignore this header. }