1: C --> S GET ... 2: C <-- S 401 Unauthorized WWW-Authenticate: NTLM 3: C --> S GET ... Authorization: NTLM <base64-encoded type-1-message> 4: C <-- S 401 Unauthorized WWW-Authenticate: NTLM <base64-encoded type-2-message> 5: C --> S GET ... Authorization: NTLM <base64-encoded type-3-message> 6: C <-- S 200 Ok
|
从交互过程可以发现,client会发送type-1消息和type-3消息给server,而server会发送type-2消息给client。
Type-1消息包括机器名、Domain等
Type-2消息包括server发出的NTLM challenge
Type-3消息包括用户名、机器名、Domain、以及两个根据server发出的challenge计算出的response,这里response是基于challenge和当前用户的登录密码计算而得
通过IE访问,上述的交互会由浏览器自动完成,微软总是有办法自己到系统里去拿到Domain、用户名、密码等等信息的,而火狐等其他浏览器就没有这么方便了,它必须要用户手工输入,当server返回401错误后,火狐会弹出该对话框让用户输入用户名、密码(在IE中,如果使用当前登录的用户名、密码验证失败后也会弹出类似的对话框)
有了NTLM HTTP认证协议,下面要实现SSO就方便多了。这时Server已经拿到client的认证信息:用户名、Domain、密码和challenge的某个运算值,这时Server只要利用这些信息连接到AD(Active Directory,活动目录)进行认证即可。
用户认证过程中Server拿到的并不是密码,而是密码的某个单向hash值,我们将通过SMB协议验证该用户身份有效性。
SMB是微软用来进行局域网文件共享和传输的协议,也称为CIFS(Common Internet File System)
首先我们看一下CIFS协议里连接和断开连接的部分:
连接:
断开连接:
在CIFS连接server(比如AD)时,首先server会发一个叫做EncryptionKey的东东给client,然后client会利用和NTLM HTTP认证中一样的算法计算出一个response给server,这个细节很关键!
因为如果http server(在这里充当CIFS的client)用这个EncryptionKey作为给http client的challenge,http client会计算出response给http server,然后http server就可以拿着这个response到AD上验证了!
现在有三个参与者了:http client,http server和AD
想象一下,首先http client发http请求给http server,为了对这个client认证,http server首先连接AD,然后就得到一个EncryptionKey,它就把这个EncryptionKey作为challenge返回给http client,然后http client会根据这个challenge和用户密码计算出response送给http server,而http server就拿着这个response到AD去认证了。
下图就表示整个这个过程:
在我们的程序中通过使用Open Source的library JCIFS完成认证过程。
JCIFS是samba组织下的一帮牛开发的一套兼容SMB协议的library,我们可以用它来在java里访问Windows共享文件,当然,既然它帮我们实现了SMB协议,那要用它来实现NTLM SSO就很容易了。
将JCIFS的library导入工程中。
创建一个Servlet Filter,继承jcifs的NtlmHttpFilter。
修改web.xml文件。
<filter> <filter-name>SSO Ntlm Filter</filter-name> <filter-class>com.eetrust.cpm.security.filters.sso.ntlm.NtlmFilter</filter-class> <init-param> <param-name>jcifs.resolveOrder</param-name> <param-value>WINS,BCAST,DNS</param-value> </init-param> <init-param> <param-name>jcifs.util.loglevel</param-name> <param-value>2</param-value> </init-param> </filter>
<filter-mapping> <filter-name>SSO Ntlm Filter</filter-name> <url-pattern>/portal/ntlm/*</url-pattern> </filter-mapping> |
NtlmFilter类的实现主要完成获取验证用户信息,验证结果的处理。