理解Django中间件的流程顺序

时间:2017-08-02 14:26:14

标签: django python-3.x django-middleware django-auth-ldap ldap3

我对Django很陌生,并且已经被分配了构建一个Web应用程序的任务,该应用程序针对LDAP对用户进行身份验证,一旦通过身份验证,用户就可以搜索某个人/组在LDAP服务器中。在过去的几周里,我一直在努力解决这个问题并wrote a few posts already,但他们没有提出任何建议。我意识到,因为我原本想在我的views.py中执行所有LDAP身份验证任务,所以这种方法不是Django的意图。相反,这些身份验证功能旨在构建为中间件,为我开辟了一整套新的蠕虫。

在任何情况下,我都意识到我在view.py中执行此整个任务受到限制,因为这会阻止用户切换到新页面并仍然在LDAP中进行身份验证(我发现{{1}的解析对象有点笨拙,see here)。

无论如何,在阅读并观看了几个信息性视频(如herehere)之后,我设法让我的中间件工作的第一步。但是,Django中有一些正在发挥作用的过程,我并不完全理解为什么。让我解释一下,但首先是相关代码:

ldap_interface.py

conn

settings.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)  

views.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',
]

urls.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')

如前所述,在进入日志记录页面,输入正确的凭据和搜索名称后,LDAP搜索会在单独的页面上返回cn,dc等信息。这是目前的意图,并将在适当的时候扩大。

控制台

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

我通过在我的代码中放置打印语句来测试发生了什么,例如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

问题:

  1. 运行"A", "B", "C", "D"命令后,在登录之前,&#34; A&#34;,&#34; B&#34;在运行中间件脚本时调用print语句,但正如您所看到的那样,它们不是一次调用,而是两次调用。为什么?
  2. 接下来的&#34; C&#34;声明被打印出来。在runserver文件中,views.py对象是通过访问中间件类(使用用户名和密码)创建的。在这个类中是conn方法,其中&#34; D&#34;声明在于。我期待这个&#34; D&#34;首先打印出来,然后继续&#34; C&#34;。但这种情况并非如此。为什么它首先是C然后D?
  3. 经过多次反复试验后,我了解到,为了开发中间件类,需要__call____init__方法。这些中间件auth类是否绝对必须包含这些方法?在目前为止我读过的任何文件中,我都没有遇到过这个特定的指示,因此必须包含这两种方法。

1 个答案:

答案 0 :(得分:0)

问题编号1)我无法解释为什么__init__被调用两次。

2)在调用视图之前执行中间件self.get_response(request)方法中的__call__之前调用的内容。在视图之后执行self.get_response(request)之后会发生什么。这解释了这种行为。这是documented here.

3)你可以使用django 2.1之后不支持的旧式中间件,否则是必需的。