2017-08-02 55 views
1

我是很新的Django,並已指派建設,對LDAP驗證用戶的Web應用程序的任務,一旦authneticated用戶可以在LDAP搜索人/組服務器。在過去的幾個星期裏,我一直在如何設置和wrote a few posts already,但他們沒有拿出任何東西。我意識到,因爲我原本想在我的views.py中執行所有LDAP身份驗證任務,所以這種方法不是Django的意圖。相反,這些認證功能旨在構建爲中間件,這爲我打開了一個全新的蠕蟲箱。理解過程順序Django的中間件

我意識到在我在執行在view.py這整個任務受到限制的任何情況下,因爲這防止了用戶切換到一個新的頁面,並且仍然保留在LDAP認證(我發現conn對象的解析有點笨拙, see here)。

無論如何,在閱讀並觀看幾個有用的視頻之後,我設法讓我的中間件工作的第一步如herehere。但是,Django中有一些進程正在工作,我不完全明白爲什麼。讓我來解釋一下,但首先相關代碼:

ldap_interface.py

try: 
    from ldap3 import Server, Connection, ALL, SUBTREE, LDAPBindError, LDAPSocketOpenError, AUTH_SIMPLE, STRATEGY_SYNC, \ 
     MODIFY_ADD, MODIFY_REPLACE, MODIFY_DELETE 
except ImportError as e: 
    raise ImportError("Import of ldap3 module unsuccessful: {0}".format(e)) 

from django.conf import settings 

########################################## 
### ERROR HANDLING ### 
logging.basicConfig(level=logging.DEBUG) 
logger = logging.getLogger(__name__) 

#Create file handler 
handler = logging.FileHandler('C:\\Users\\me\\Desktop\\LDAP_auth.log') 
handler.setLevel(logging.DEBUG) 

#Create logging format 
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 
handler.setFormatter(formatter) 

#Add handlers to the logger 
logger.addHandler(handler) 
########################################## 

class LDAP_Auth_Backend: 
    def __init__(self, get_response, un=None, pw=None): 
     print("A:{0}".format(get_response)) 
     self.get_response = get_response 
     print("B:{0}".format(self.get_response)) 

    def __call__(self, request): 
     response = self.get_response(request) 
     print("D:{0}".format(response)) 
     return response 

    """ 
    def process_request(self, request): 
     print ("Middleware executed") 
    """ 

    def ldap_connect(self, un=None, pw=None):  
     try: 
      # Define the server 
      server = Server(settings.LDAP_SERVER, get_info=ALL) 
      # Connection and Bind operation 
      conn = Connection(server, user=un, password=pw, \ 
          auto_bind=True,check_names=True) 
      conn.start_tls() # Session now on a secure channel. 
      return conn 

     except LDAPBindError as e: 
      print ("LDAPBindError, credentials incorrect: {0}".format(e)) 
      logger.debug("LDAPBindError, credentials incorrect: {0}".format(e)) 
      return HttpResponse('Invalid login') 
     except LDAPSocketOpenError as e: 
      print ("LDAPSocketOpenError, LDAP Server connection error: {0}".format(e)) 
      logger.debug("LDAPSocketOpenError, LDAP Server connection error: {0}".format(e)) 
      sys.exit(1) 

settings.py

MIDDLEWARE = [ 
    'django.middleware.security.SecurityMiddleware', 
    'django.contrib.sessions.middleware.SessionMiddleware', 
    'django.middleware.common.CommonMiddleware', 
    'django.middleware.csrf.CsrfViewMiddleware', 
    'django.contrib.auth.middleware.AuthenticationMiddleware', 
    'django.contrib.messages.middleware.MessageMiddleware', 
    'django.middleware.clickjacking.XFrameOptionsMiddleware', 
    'UserAuth.Middleware.ldap_interface.LDAP_Auth_Backend', 
] 

views.py

def ldap_authentication(request):    
    if request.POST: 
     username = request.POST['username'] 
     LDAP_MODIFY_PASS = request.POST['password'] 
     searchFilter = request.POST['searchUser'] 
     LDAP_AUTH_SEARCH_DN = '{}\\{}'.format(DOMAIN_NAME, username) # Should be the same as conn.extend.standard.who_am_i() 
     conn = LDAP_Auth_Backend.ldap_connect(request, un=LDAP_AUTH_SEARCH_DN, pw=LDAP_MODIFY_PASS) 
     print ("C: {0}".format(conn)) 

     conn.search(
       search_base=LDAP_AUTH_SEARCH_BASE, 
       search_filter= '(cn={})'.format(searchFilter), # This is the user being searched for 
       search_scope=SUBTREE # Other parameters are BASE & LEVEL 
       ) 
     entry = conn.entries[0] 
     dn = entry._dn 
     split_dn = dn.split(",") 
     request.session.set_expiry(10) 
     return render(request, 'search_page.html', {'dn':split_dn}) 

    return render(request, 'login.html') 

urls.py

urlpatterns = [ 
    url(r'^admin/', admin.site.urls), 
    url(r'^$', ldap_authentication, name='index'), 
] 

如前所述,得到的登錄頁面,輸入正確的憑據和搜索名稱後,LDAP搜索回來了一個單獨的CN,DC等信息。頁。這是迄今爲止的打算,並將在適當的時候擴大。

控制檯

System check identified no issues (0 silenced). 
August 02, 2017 - 15:44:30 
Django version 1.11, using settings 'UserAuth.settings' 
Starting development server at http://127.0.0.1:8000/ 
Quit the server with CTRL-BREAK. 
A:<function BaseHandler._get_response at 0x000000B93CE89C80> 
B:<function BaseHandler._get_response at 0x000000B93CE89C80> 
A:<function BaseHandler._get_response at 0x000000B93B863378> 
B:<function BaseHandler._get_response at 0x000000B93B863378> 
C: ldap://an_ip:389 - cleartext - user: random_name - bound - open - <local: another_ip:55135 - remote: etc> - tls started - listening - SyncStrategy 
D:<HttpResponse status_code=200, "text/html; charset=utf-8"> 
[02/Aug/2017 15:44:41] "POST/HTTP/1.1" 200 1867 
[02/Aug/2017 15:44:41] "GET /static/bourbon HTTP/1.1" 404 1634 

我測試通過將打印語句在我的代碼發生了什麼事,那就是你可以看到例如"A", "B", "C", "D"

問題:

  1. 一旦runserver命令運行,並在中,「A」登陸前,「B」打印語句稱爲,當中間件劇本跑,但你可以看到他們不是一次,而是兩次。爲什麼?
  2. 接下來打印「C」語句。在views.py文件中,conn對象是通過訪問中間件類(使用用戶名和密碼)創建的。在這個類中是「D」語句所在的方法__call__。我預計這個「D」首先被打印出來,然後繼續到「C」。但這種情況並非如此。爲什麼首先是C然後是D?
  3. 我瞭解了很多試錯之後,爲了開發中間件類,需要使用__init____call__方法。這些方法絕對必須包含在這個中間件認證類中嗎?迄今爲止我讀過的任何文檔中都沒有遇到這個特定的指令,因此必須包含這兩種方法。

回答

0

問題編號1)我無法解釋爲什麼__init__被調用兩次。

2)在調用視圖之前執行中間件的__call__方法中的self.get_response(request)之前調用的任何東西。在視圖後執行self.get_response(request)之後會發生什麼。這解釋了這種行爲。這是documented here.

3)你可以使用django 2.1後不支持的老式中間件,否則是必需的。