简体   繁体   中英

NTLM Authentication with Active Directory using Java

I was reading this article , with the help of it I am able to find the username, domain name and hostname of the machine accessing my JSP Page, But Still I can't understand how to authenticate the user. Because in Firefox when I am accessing my JSP page and try to enter correct Username but wrong password, it authenticates the user.

So the prime concern is how can I authenticate the user with NTLM Protocol, ie once I have the username and password, I can make a LDAP request to authenticate the user, but here only Username of the person is known to server.

<%

String auth = request.getHeader("Authorization");
/*
 * Client to Server - Get Page.
 */
if (auth == null) {

    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    response.setHeader("WWW-Authenticate", "NTLM");
    return;
}
/*
 * Client to Server 
 GET ...
 Authorization: NTLM <base64-encoded type-1-message>

Type 1 Message - 
        0       1       2       3
        +-------+-------+-------+-------+
    0:  |  'N'  |  'T'  |  'L'  |  'M'  |
        +-------+-------+-------+-------+
    4:  |  'S'  |  'S'  |  'P'  |   0   |
        +-------+-------+-------+-------+
    8:  |   1   |   0   |   0   |   0   |
        +-------+-------+-------+-------+
    12:  | 0x03  | 0xb2  |   0   |   0   |
        +-------+-------+-------+-------+
    16:  | domain length | domain length |
        +-------+-------+-------+-------+
    20:  | domain offset |   0   |   0   |
        +-------+-------+-------+-------+
    24:  |  host length  |  host length  |
        +-------+-------+-------+-------+
    28:  |  host offset  |   0   |   0   |
        +-------+-------+-------+-------+
    32:  |  host string                  |
        +                               +
        .                               .
        .                               .
        +             +-----------------+
        |             | domain string   |
        +-------------+                 +
        .                               .
        .                               .
        +-------+-------+-------+-------+
 */
if (auth.startsWith("NTLM ")) {



    byte[] msg = new sun.misc.BASE64Decoder().decodeBuffer(auth
            .substring(5));
    int off = 0, length, offset;
    String s;
    s = new String(msg, 0, msg.length);

    if (msg[8] == 1) {

        off = 18;

        byte z = 0;
        /*
                    0       1       2       3
         +-------+-------+-------+-------+
     0:  |  'N'  |  'T'  |  'L'  |  'M'  |
         +-------+-------+-------+-------+
     4:  |  'S'  |  'S'  |  'P'  |   0   |
         +-------+-------+-------+-------+
     8:  |   2   |   0   |   0   |   0   |
         +-------+-------+-------+-------+
    12:  |   0   |   0   |   0   |   0   |
         +-------+-------+-------+-------+
    16:  |  message len  |   0   |   0   |
         +-------+-------+-------+-------+
    20:  | 0x01  | 0x82  |   0   |   0   |
         +-------+-------+-------+-------+
    24:  |                               |
         +          server nonce         |
    28:  |                               |
         +-------+-------+-------+-------+
    32:  |   0   |   0   |   0   |   0   |
         +-------+-------+-------+-------+
    36:  |   0   |   0   |   0   |   0   |
         +-------+-------+-------+-------+
         */
        byte[] msg1 = { (byte) 'N', (byte) 'T', (byte) 'L', (byte) 'M', 
                        (byte) 'S', (byte) 'S', (byte) 'P', z,
                        (byte) 2, z, z, z, 
                        z, z, z, z, 
                        (byte) 40, z, z, z,
                        (byte) 1, (byte) 130, z, z, 
                        (byte) 1, (byte) 9, (byte) 0, (byte) 9, 
                        (byte) 1, (byte) 9, (byte) 8, (byte) 9,  
                        z, z, z, z, 
                        z, z, z, z };
        // 
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setHeader("WWW-Authenticate", "NTLM "
                + new sun.misc.BASE64Encoder().encodeBuffer(msg1)
                        .trim());
        return;
    } 
    /*
     * Client sending type 3 message.

        0       1       2       3
        +-------+-------+-------+-------+
    0:  |  'N'  |  'T'  |  'L'  |  'M'  |
        +-------+-------+-------+-------+
    4:  |  'S'  |  'S'  |  'P'  |   0   |
        +-------+-------+-------+-------+
    8:  |   3   |   0   |   0   |   0   |
        +-------+-------+-------+-------+
   12:  |  LM-resp len  |  LM-Resp len  |
        +-------+-------+-------+-------+
   16:  |  LM-resp off  |   0   |   0   |
        +-------+-------+-------+-------+
   20:  |  NT-resp len  |  NT-Resp len  |
        +-------+-------+-------+-------+
   24:  |  NT-resp off  |   0   |   0   |
        +-------+-------+-------+-------+
   28:  | domain length | domain length |
        +-------+-------+-------+-------+
   32:  | domain offset |   0   |   0   |
        +-------+-------+-------+-------+
   36:  |  user length  |  user length  |
        +-------+-------+-------+-------+
   40:  |  user offset  |   0   |   0   |
        +-------+-------+-------+-------+
   44:  |  host length  |  host length  |
        +-------+-------+-------+-------+
   48:  |  host offset  |   0   |   0   |
        +-------+-------+-------+-------+
   52:  |   0   |   0   |   0   |   0   |
        +-------+-------+-------+-------+
   56:  |  message len  |   0   |   0   |
        +-------+-------+-------+-------+
   60:  | 0x01  | 0x82  |   0   |   0   |
        +-------+-------+-------+-------+
   64:  | domain string                 |
        +                               +
        .                               .
        .                               .
        +           +-------------------+
        |           | user string       |
        +-----------+                   +
        .                               .
        .                               .
        +                 +-------------+
        |                 | host string |
        +-----------------+             +
        .                               .
        .                               .
        +   +---------------------------+
        |   | LanManager-response       |
        +---+                           +
        .                               .
        .                               .
        +            +------------------+
        |            | NT-response      |
        +------------+                  +
        .                               .
        .                               .
        +-------+-------+-------+-------+
     */
    else if (msg[8] == 3) {

        off = 30;
        length = msg[off + 17] * 256 + msg[off + 16];
        offset = msg[off + 19] * 256 + msg[off + 18];

        s = new String(msg, offset, length);

        System.out.println("Host String - " + s + " ");
    } else{

        return;
    }
    /*
     * Reading domain information
     */
    length = msg[off + 1] * 256 + msg[off];
    offset = msg[off + 3] * 256 + msg[off + 2];     
    s = new String(msg, offset, length);
    System.out.println("Domain Name - " + s +  " ");

    /*
     * Reading User Name information.
     */
    length = msg[off + 9] * 256 + msg[off + 8];
    offset = msg[off + 11] * 256 + msg[off + 10];

    s = new String(msg, offset, length);
    System.out.println("User Name - " + s + " ");

In the following thread they put the Tomcat behind an Apache Server and use an Apache Module to perform the NTLM authentication.

Spring 3 and NTLM authentication

NTLM authentication does not use a password, it uses a challenge-response protocol which requires a few server roundtrips.

In the second GET request, you respond with a server 'nonce' which is the authentication challenge received from the domain controller. On the third GET, you get the authentication response which you can validate with the challenge via the domain controller.

In your code, you use a hard-coded challenge (0x19091989), and completely ignore the response.

JCIFS has an implementation that actually finds a domain controller to handle the challenge and response in http://code.google.com/p/jcifs-fork/source/browse/trunk/jcifs/src/jcifs/http/NtlmHttpFilter.java . You could reverse engineer this, or use the filter 'an sich' as described in http://jcifs.samba.org/src/docs/ntlmhttpauth.html . AFAIK this only works on a Windows server, but I could be mistaken.

您所要做的就是按照这篇文章使Firefox能够使用像IE和Chrome那样的NTLM!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM