浏览器未响应服务器发送的 NTLM 质询

问题描述

我们在我们的产品中使用最新的 JCIFS jars (org.codelibs-jcifs-2.1.19) 进行 NTLM 身份验证。
迁移到较新的 JCIFS 版本的原因是因为旧版本使用 SMB1,该版本已被弃用。
客户不愿意切换到 Kerberos 或类似的高级身份验证方案,并希望坚持使用 NTLM。

在我们使用最新的 JCIFS jar 之后,我们遇到的问题是浏览器没有响应来自服务器的 NTLM Challenge。
以下是 req-res 的年表。

  1. 浏览器 - 资源请求 /hello
  2. 服务器 - 401 WWW 验证 NTLM
  3. 浏览器 - 授权 NTLM 协商
  4. 服务器 - 401 WWW 验证 NTLM 挑战
  5. 浏览器 - 没有进一步的请求发送到服务器。

这是wireshark的截图

NTLM Request Response


以下是我的源代码(其中一些仅从 stackoverflow 复制)-

public class NTLMAuthenticator {
    private CIFSContext cifsContext;
    private Properties properties;
    private String domainName;
    private String clientUserName;
    private String clientPassword;
    private NtlmPasswordAuthenticator ntlmPasswordAuthenticator;
    private sspContext context;

    public NTLMAuthenticator(String domainName,String clientUserName,String clientPassword) throws CIFSException {
        this.domainName = domainName;
        this.clientUserName = clientUserName;
        this.clientPassword = clientPassword;
        initProperties();
        ntlmPasswordAuthenticator = new NtlmPasswordAuthenticator(domainName,clientUserName,clientPassword);
        cifsContext = new BaseContext(new PropertyConfiguration(properties));
    }

    private byte[] extractTokenFromrequest(HttpServletRequest request) throws UnsupportedEncodingException,Base64DecodingException {
        String header = request.getHeader("Authorization");

        if ((header != null)) {
            System.out.println("Received Negotiate Header for request " + request.getRequestURL() + ": " + header);
            byte[] base64Token = header.replace("NTLM","").trim().getBytes("US-ASCII");
            byte[] decodedToken = Base64.decode(base64Token);
            return decodedToken;
        } else return null;
    }

    private void negotiate(HttpServletResponse response) throws UnsupportedEncodingException {
        String redirectHeader = "NTLM";
        response.setHeader("Connection","Keep-Alive");
        response.setHeader("WWW-Authenticate",redirectHeader);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    }

    private void sendType2Response(HttpServletResponse response,byte[] tokenFromrequest) throws Exception {
        Type2Message type2Message = new Type2Message(cifsContext,(Type1Message) constructNTLMMessage(tokenFromrequest),domainName.getBytes("US-ASCII"),null);
        
        String msg = new String(Base64.encode(type2Message.toByteArray()));
        response.setHeader("Connection","NTLM " + msg);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentLength(0);
        response.flushBuffer();
        return;
    }

    public void authenticate(HttpServletRequest request,HttpServletResponse response) throws Exception {
        byte[] tokenFromrequest = extractTokenFromrequest(request);

        // step 0 - Send Negotiate header
        if (tokenFromrequest == null || tokenFromrequest.length == 0) {
            negotiate(response);
            return;
        }

        // step 1 - client sends negotiation token,which is called as Type1Token
        NtlmMessage message = constructNTLMMessage(tokenFromrequest);
        if (message instanceof Type1Message) {
            // create new context here - LOL not thread safe of course !
            context = ntlmPasswordAuthenticator.createContext(cifsContext,domainName,null,false);
            byte[] type1TokenBytes = context.initSecContext(tokenFromrequest,tokenFromrequest.length);  //type2 message is created and returned as byte array

            sendType2Response(response,tokenFromrequest);
            return;
        }

        // step 2 - client sends type3 message,which contains actual information
        byte[] type1TokenBytes = context.initSecContext(tokenFromrequest,tokenFromrequest.length);
        context.dispose(); // auth is completed

        System.out.println(context);
    }


    // The Client will only ever send a Type1 or Type3 message ... try 'em both
    protected NtlmMessage constructNTLMMessage(byte[] token) {
        NtlmMessage message = null;
        try {
            message = new Type1Message(token);
            return message;
        } catch (IOException e) {
            if ("Not an NTLMssp message.".equals(e.getMessage())) {
                return null;
            }
        }

        try {
            message = new Type3Message(token);
            return message;
        } catch (IOException e) {
            if ("Not an NTLMssp message.".equals(e.getMessage())) {
                return null;
            }
        }
        return message;
    }
}

非常感谢任何帮助。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)