问题描述
我们在我们的产品中使用最新的 JCIFS jars (org.codelibs-jcifs-2.1.19)
进行 NTLM 身份验证。
迁移到较新的 JCIFS 版本的原因是因为旧版本使用 SMB1,该版本已被弃用。
客户不愿意切换到 Kerberos 或类似的高级身份验证方案,并希望坚持使用 NTLM。
在我们使用最新的 JCIFS jar 之后,我们遇到的问题是浏览器没有响应来自服务器的 NTLM Challenge。
以下是 req-res 的年表。
- 浏览器 - 资源请求 /hello
- 服务器 - 401 WWW 验证 NTLM
- 浏览器 - 授权 NTLM 协商
- 服务器 - 401 WWW 验证 NTLM 挑战
- 浏览器 - 没有进一步的请求发送到服务器。
这是wireshark的截图
以下是我的源代码(其中一些仅从 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 (将#修改为@)