zoukankan      html  css  js  c++  java
  • NodeJs NTLM认证(烂尾)

    正在做的一个公司内部网站,需要用到域认证,无奈,只得学习相关的知识,边学边写:

    1. NodeJs的有一个服务器端做认证库的非常好,今天花了半天的时间,简单读了一下代码,觉得设计的不错,很容易扩展,库的名字是connect-auth,看名字就知道是expressJs的一个中间件,等有空就把一些源码阅读的笔记贴上来。

    2. 接下来就是记录一些域认证相关的知识:

    Windows的域认证,有2个认证方式:NTLM和Kerberos,NTLM很早就说要被淘汰,但是目前好像还是活的好好的,假如服务器端允许Negotiate,那么IE浏览器会有不同选择,参见:http://blog.csdn.net/yangxin114/article/details/8111958。

    简单起见,我就不使用,Negotiate方式了,强制使用NTLM认证。

    要启用NTLM认证,服务器端在接收到客户端请求时,返回401 (Unauthorized)并附加 WWW-Authenticate头,值为"NTLM"就行了 (如果要Negotiate,那么就多发一个WWW-Authenticate头,值为Negotiate),客户端接收到服务器的回复之后,浏览器应该会弹出认证的窗口,让用户输入域账号和密码,然后会送一个请求,并附加Authorization头,就这样进入了3次握手的过程(type1,type2,type3)。


    更新:nodejs的NTLM好实现,只要根据协议生成hash值就行了,但问题是,服务器端没有办法拿到该用户的域账号密码来核实!查了一下,貌似只有一种方法:使用SMB,CIFS(这篇文章写的很好,NTLM SSO的实现:http://blog.csdn.net/zoufeiyy/article/details/1694634),但是鉴于Nodejs没有现成的库,实现该协议,所以我放弃了!准备换条路走走,那就是iisnode~。


    附那篇文章的内容:

    http://blog.csdn.net/zoufeiyy/article/details/1694634

    NTLM SSO的实现

    最近项目中要求实现Web应用的SSO(Single Sign On),即对于已经登录到Windows Domain中的用户,不需要输入用户名、密码而直接使用当前登录的Domain用户信息进行验证,如果验证成功则进入,否则拒绝进入。

     

    在网上搜了一些资料,同时也对NTLM的认证方式有了些了解,记录之。

     

    NTLM HTTP认证

    过程如下:

     

        1: C  --> S   GET ...

       

        2: C <--  S   401 Unauthorized

                     WW-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和当前用户的登录密码计算而得

     

    具体细节参考下面两个网址:

    http://www.innovation.ch/personal/ronald/ntlm.html

    http://davenport.sourceforge.net/ntlm.html#whatIsNtlm

     

    注:

    在IE里,上述的交互会由浏览器自动完成,M$总是有办法自己到OS里去拿到Domain、用户名、密码等等信息的,而FF就没有这么方便了,它必须要用户手工输入,当server返回401错误后,FF会弹出该对话框让用户输入用户名、密码(在IE中,如果使用当前登录的用户名、密码验证失败后也会弹出这样的对话框)

     

     

    OK,有了NTLM HTTP认证协议,下面要实现SSO就方便多了。这时server已经拿到client的认证信息:用户名、Domain、密码和challenge的某个运算值,这时server只要利用这些信息连接到AD(Active Directory,活动目录)(或者其他认证服务器)进行认证即可。

     

    但这里还有个问题,因为server拿到的并不是密码,而是密码的某个单向hash值,那怎么用这个信息到AD上认证呢?

     

    答案是SMB(Server Message Block)!

     

    SMB是M$用来进行局域网文件共享和传输的协议,也称为CIFS(Common Internet File System),CIFS协议的细节可以在MSDN上查到:

    http://msdn2.microsoft.com/en-us/library/aa302240.aspx

    也可以到samba上去看看最新的一些发展:

    http://www.samba.org/

     

    我们着重看一下CIFS协议里连接和断开连接的部分:

     

     

    连接:

     

     

     

    断开连接:

     

     

    OK,看起来蛮复杂的,不过没关系,关键我们要知道,在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去认证了J

     

    下图就表示整个这个过程:

     

     

    现在,我们已经有足够的理论武装起来可以实现SSO了,但是,难道要我们自己去实现这些协议吗?当然可以,有兴趣可以尝试一下J

    不过另一个选择是使用Open Source的library,jCIFS就是干这些事情的。

     

    jCIFS是samba组织下的一帮牛开发的一套兼容SMB协议的library,我们可以用它来在java里访问Windows共享文件,当然,既然它帮我们实现了SMB协议,那要用它来实现NTLM SSO就很容易了。

    http://jcifs.samba.org/

    在这个网址可以下载到jCIFS的source code和library

     

    好,现在可以休息一下了,我们通过一个例子step by step看一下jCIFS怎么来实现SSO吧。

    1.       把jcifs-1.2.13.jar放到tomcat的webapp目录

    2.       创建一个web.xml,用于创建一个servlet filter,处理http连接(记得把里面的ip地址替换为你自己的AD server的ip地址)

    <web-app xmlns="http://java.sun.com/xml/ns/javaee"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

       version="2.5">

      <display-name>Welcome to Tomcat</display-name>

      <description>

         Welcome to Tomcat

      </description>

      <filter>

        <filter-name>NtlmHttpFilter</filter-name>

        <filter-class>jcifs.http.NtlmHttpFilter</filter-class>

        <init-param>

            <param-name>jcifs.http.domainController</param-name>

            <param-value>10.28.1.212</param-value>

        </init-param>

        <init-param>

        <param-name>jcifs.util.loglevel</param-name>

        <param-value>6</param-value>

        </init-param>

      </filter>

      <filter-mapping>

        <filter-name>NtlmHttpFilter</filter-name>

        <url-pattern>/*</url-pattern>

      </filter-mapping>

    </web-app>

     

    3.       重新启动tomcat,打开http://localhost:8080/,如果用的IE,就会自动使用当前用户进行验证,而如果使用FF,就会弹出对话框,输入用户名密码后就可以验证通过,看到tomcat的页面了

     

    这个例子够简单的,jCIFS应用也确实非常简单了,当然如果你要实现一些其他特性,比如根据当前登录的用户账户决定用户的权限、以及看到页面的内容,那你就必须通过jCIFS的API去操作了,可以参考jCIFS的API文档:

    http://jcifs.samba.org/src/docs/api/

     

     

    最后,说点这个方案的问题和不足吧,

    -          首先由于jCIFS只是应用了SMB协议进行认证,这样它就没办法拿到用户的其他的一些信息,比如组信息或者权限信息。对于这个问题,一般可以由我们自己的应用程序通过LDAP到AD上去存取,但毕竟增加了我们的工作。

    -          第二个不足是,NTLM认证是一个M$准备放弃的协议,在Windows 2000和以后的操作系统中,缺省的认证协议是Kerberos,只有在和2000之前的系统通信时才使用NTLM。当然这并不是说jCIFS在2000以上就用不起来了,缺省情况总是可以用的,M$总是要保持兼容的J 当然如果你想实现基于Kerberos的SSO,你可以去参考下面列出的文章,但这就不是这里讨论的话题了。

    http://free.tagish.net/jaas/

    http://java.sun.com/j2se/1.4.2/docs/guide/security/jgss/single-signon.html



  • 相关阅读:
    JavaScript实现HTML导航栏下拉菜单[悬浮显示]
    Paper Pal:一个中英文论文及其代码大数据搜索平台
    小程序定位地图模块全系列开发教学(超详细)
    简单的个人介绍网页主页面【附代码】
    前端分页功能(通用)
    打造完美写作系统:Gitbook+Github Pages+Github Actions
    移动端布局
    三剑客var,let,const
    包含多个段的程序01 零基础入门学习汇编语言29
    更灵活的定位内存地址的方法01 零基础入门学习汇编语言32
  • 原文地址:https://www.cnblogs.com/puncha/p/3876908.html
Copyright © 2011-2022 走看看