目标:使用域账户单点登录,并且IE浏览器能自动登录
查相关资料,知道HTTP协议中有401这个状态表示用户未授权,要求NTLM方式提供用户信息时,IE会自动取当前windows账户,其他浏览器会弹出对话框要求用户输入。服务器得到用户信息后交给域控验证。
以上是原理,IIS服务器是提供这样的实现,只需简单配置。但java服务器,如tomcat、jboss,都没有这样的功能,但在网上很容易搜到了一个第三方的开源项目实现了这个功能,他是jcifs
使用了最新版1.3.14,使用中首先出现了怪现象,问题A:jboss启动后,第一个发出请求的浏览器能正常进行当前用户的验证,以后的都报用户名或密码错误。
调试了好久,最终在每次验证完成后disconnect一下,问题就解决了。
好景不长,新问题出现了,问题B:项目在公司内部上线后,不停有用户登录不上,报500错误:the parameter is incorrect
为解决问题B,找了域控相关负责人,他提供了一些jcifs的配置方式,包括说在配置中要指定一个连接域控的用户名密码,但所有配置都不见效,最后他给了一个另外的域控IP,换上后居然不再有人报500错误了。
据了解,这个不出错的域控是老版本的,2003系统,出错的域控是2008系统。问题算是解决了吗?没有。因为公司马上要将所有域控都升级了,到时没有老版的域控给我们系统验证了。
查找解决方案途中,对问题A有了新发现,其实不用改代码,只要在配置中指定一个连接域控的用户名密码即可。虽然不知道原因。
再后来,一个同学通过抓包,发现了出500错的规律,查资料后,怀疑出错用户都是使用vista或win7用户,并得到了证实。原因是vista或win7默认采用NTLM2协议验证,JCIFS不支持NTLM2。
但或许你会问,这跟新老域控有什么关系。这样解释:首先老版本域控也不支持NTLM2或默认采用了NTLM协议;其次,vista和win7系统默认采用HTLM2并在服务器要求NTLM是也兼容NTLM。这样vista和win7用户在老域控下就使用了NTLM验证,在新域控下使用了NTLM2验证。
根据这位提供的宝贵资料,Google关键字 NTLM2 jcifs,果然找到了答案
http://jcifs.samba.org/FAQ.html#ntlmv2
http://www.ioplex.com/
引用
Q: Does jCIFS support NTLMv2?
A: Yes. As of 1.3.0, JCIFS fully supports NTLMv2 and uses it by default.
Note: The NTLM HTTP SSO Filter that used to be included with JCIFS cannot support NTLMv2. See the above question for details.
引用
Q: How do I do NTLM HTTP authentication (a.k.a Single Sign On) for my website?
A: See "Jespa"
引用
Jespa is a complete NTLM implementation in 100% Java that properly implements both the server and client side of NTLMv2, NTLMv1, NTLM2 Session Security and Key Exchange. Of particular interest to users of the old JCIFS SSO Filter, Jespa can properly authenticate NTLMv2 clients just like a Windows server does (using the NetrLogonSamLogon DCERPC call over NETLOGON w/ Secure Channel) and it includes an HTTP SSO Servlet Filter.
Note: Jespa uses JCIFS for DCERPC transport and some basic client side NTLM calculations. Jespa is not Open Source (although it is free for up to 25 users). Please contact IOPLEX Software support if you have any questions regarding Jespa.
但这个不是开源的,也可能是有限制的(free for up to 25 users),于是有自己修改jcifs的冲动。
但是...
引用
Note: The old SSO Filter that used to be included with JCIFS used a "man in the middle" technique that cannot support NTLMv2 and has therefore been removed from the JCIFS package.
为什么呢?
引用
Now regarding your question about the HTTP Filter not supporting NTLMv2:
The JCIFS NTLM HTTP Authentication Filter uses a "man in the middle"
technique whereby the web server simply marshals the NTLM
challenge-response tokens between the browser and an SMB server.
Specifically, when the browser triggers the Filter, JCIFS connects to
an SMB server (the "domain controller") and returns the "server
challenge" for that SMB connection to the browser. The browser
computes NTLM password hashes using the DC's challenge and the user's
password and sends them to the Filter which just forwards them to the
SMB server as if it were authenticating itself. Meaning, the browser
thinks it's authenticating with the web server when it's really
authenticating directly with the DC.
This "man in the middle" technique works fine with NTLMv1 however it
cannot work with NTLMv2. The NTLMv2 challenge also consists of "target
information" that includes the hostname of the target (among other
things). That target information is factored into the calculations of
the password hashes. So the problem is that target information for the
web server is different from the target information for the domain
controller and therefore the password hash computation will be wrong
as the browser will expect target information for the web server and
not the domain controller. This is done specifically to thwart "man in
the middle" scenarios. And thus the technique used by the JCIFS NTLM
HTTP Filter cannot be used with NTLMv2.
Finally, the JCIFS NTLM HTTP Authentication Filter is a hack. It is a
hack that has worked quite well and to our knowledge is secure but it
is not a correct implementation of an NTLM authentication acceptor. A
proper implementation would do as IIS would do which in the case of
NTLM would be to use the DCERPC NETLOGON service. Specifically, the
web server would generate it's own random challenge with the proper
target information, send that to the browser, collect the password
hashes and then call the NetrSamLogon RPC with the challenge that we
generated and the corresponding password hashes. Or, according to the
Heimdal project, it is also possible to use a form of Kerberos digest
authentication to validate this information.
看来要支持NTLM2方式的域验证,不是简单的修改jcifs,而是要去实现一个协议。况且这方面文档又很难找,真的是不太可能。
最后只能是折中办法:要么域控采用NTLM,要么客户端NTLM,就是不能两者都是NTLM2。域控降级到NTLM的方案被否定,不能为了一个应用而影响所有。所以只能限定客户端使用NTLM。
我们采用当出错时显示帮助页面的方式,引导用户修改本地配置LM身份认证级别。可以在本地安全此略里配置,或者提供一个注册表文件,将HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\LMCompatibilityLevel 修改为1即可