wiki身份验证流

警告

自从Pyramid1.0发布后,这个配方还没有收到重大的更新。从那时起,这个食谱所指的wiki教程已经收到了许多重要的更新。Pyramid1.6.1于2016年2月2日发布,并为Pyramid1.7版合并了wiki教程的主要更新。Pyramid1.7发布后,此配方将作为废弃配方删除。

本教程描述了完成 Adding authorization 来自主 Pyramid 文档的教程章节。

本文由约翰·希普曼撰写。

认证的总体流程

现在您已经看到了身份验证机制的所有部分,下面是一些示例,展示了它们如何一起工作。

  1. 登录失败:用户请求 /FrontPage/edit_page . 网站显示登录表单。用户进入 editor as the login, but enters an invalid password bad . 网站会重新显示登录表单,并显示消息“登录失败”。见 登录失败 .

  2. 用户再次请求 /FrontPage/edit_page . 网站显示登录表单,这次用户进入登录 editor 密码 editor . 网站显示编辑表单,其中包含 /FrontPage . 用户进行了一些更改并保存它们。见 Successful login .

  3. 用户再次访问 /FrontPage/edit_page . The site goes immediately to the edit form without requesting credentials. 见 验证后重新访问 .

  4. 用户单击 Logout 链接。见 注销 .

登录失败

当用户输入url时,进程启动。 http://localhost:6543/FrontPage/edit_page . 假设这是对应用程序的第一个请求,页面数据库是空的,除了 Page 为首页创建的实例 initialize_sql 中的函数 models.py .

这个过程涉及两个完整的请求/响应周期。

  1. 从首页,用户单击 Edit page . 请求是 /FrontPage/edit_page . 可调用的视图是 login.login . 答案是 login.pt 带空白字段的模板。

  2. 用户输入无效的凭据并单击 Log in . 一 POST 请求发送到 /FrontPage/edit_page . 视图调用是 login.login . 答案是 login.pt 显示“登录失败”消息的模板,输入字段显示其以前的值。

循环1:

  1. 在URL调度期间,路由 '/{{pagename}}/edit_page' 考虑匹配。关联视图具有 view_permission='edit' 附加了权限,因此调度逻辑必须验证用户是否具有该权限,或者认为路由不匹配。

    所有路由匹配的上下文来自配置的根工厂, RootFactory() 在里面 models.py . This class has an __acl__ 定义所有路由的访问控制列表的属性:

    __acl__ = [ (Allow, Everyone, 'view'),
                (Allow, 'group:editors', 'edit') ]
    

    实际上,这意味着对于任何需要 edit 权限,用户必须经过身份验证,并具有 group:editors 主体或路由不被认为匹配。

  2. 要查找用户主体的列表,授权优先策略将检查用户是否具有 paste.auth.auth_tkt 饼干。由于用户从未访问过该站点,因此不存在此类cookie,并且该用户被视为未经身份验证。

  3. 由于用户未经身份验证,因此 groupfinder 中的函数 security.py 被调用 None 作为其 userid 争论。函数返回一个空的主体列表。

  4. 因为该列表不包含 group:editors 校长 '/{{pagename}}/edit_page' 路线 edit 权限失败,路由不匹配。

  5. 因为没有匹配的路由, forbidden view 调用callable: login 模块中的功能 login.py .

  6. 里面 login function, the value of login_urlhttp://localhost:6543/login 以及 referrerhttp://localhost:6543/FrontPage/edit_page .

    因为 request.params 没有键 'came_from' 变量 came_from 也设置为 http://localhost:6543/FrontPage/edit_page . 变量 messageloginpassword 设置为空字符串。

    因为 request.params 没有键 'form.submitted' , the login 函数返回此字典:

    {'message': '', 'url':'http://localhost:6543/login',
     'came_from':'http://localhost:6543/FrontPage/edit_page',
     'login':'', 'password':''}
    
  7. 此字典用于呈现 login.pt 模板。在形式上, action 属性是 http://localhost:6543/login 以及 came_from 在该表单中作为隐藏字段包含在模板的该行中::

    <input type="hidden" name="came_from" value="${came_from}"/>
    

循环2:

  1. 用户输入不正确的凭据并单击 Log in 按钮,用于 POST 请求到URL http://localhost:6543/login . 的名字 Log in 此表单中的按钮是 form.submitted .

  2. 有图案的路线 '/login' 与此URL匹配,因此控件再次传递给 login 查看可调用。

  3. 这个 login_urlreferrer 这次有相同的价值( http://localhost:6543/login 如此多变 referrer 设置为 '/' .

    自从 request.params 有键 'form.submitted' 的价值观 loginpassword 检索自 request.params .

    因为登录名和密码与 USERS 词典在 security.py 变量 message 设置为 'Failed login' .

    View Callable返回此字典:

    {'message':'Failed login',
     'url':'http://localhost:6543/login', 'came_from':'/',
     'login':'editor', 'password':'bad'}
    
  4. 这个 login.pt 使用这些值呈现模板。

Successful login

在此方案中,用户再次请求URL /FrontPage/edit_page .

这个过程包括四个完整的请求/响应周期。

  1. 用户点击 Edit page . 可调用的视图是 login.login . 响应是模板 login.pt ,所有字段为空。

  2. The user enters valid credentials and clicks Log in . 可调用的视图是 login.login . 响应是重定向到 /FrontPage/edit_page .

  3. 可调用的视图是 views.edit_page . The response renders template edit.pt ,显示当前页面内容。

  4. The user edits the content and clicks Save . 可调用的视图是 views.edit_page . 响应是重定向到 /FrontPage .

执行收益 登录失败 ,直到密码 editor 与中的值成功匹配 USERS 字典。

循环2:

  1. login.login 视图可调用,值 login_urlhttp://localhost:6543/login 以及 referrer'/'came_fromhttp://localhost:6543/FrontPage/edit_page 执行此块时:

    if USERS.get(login) == password:
        headers = remember(request, login)
        return HTTPFound(location=came_from, headers=headers)
    
  2. 因为这次密码匹配, pyramid.security.remember 返回将设置 paste.auth.auth_tkt 用户浏览器中用于登录的身份验证cookie 'editor' .

  3. 这个 HTTPFound 异常返回将浏览器重定向到的响应 http://localhost:6543/FrontPage/edit_page 包括设置身份验证cookie的头。

循环3:

  1. 路由模式 '/{{pagename}}/edit_page' 匹配此URL,但相应的视图受 'edit' 许可。

  2. 因为用户现在有一个身份验证cookie将其登录名定义为 'editor' , the groupfinder 以该值作为函数的 userid 争论。

  3. 这个 groupfinder 函数返回列表 ['group:editors'] . 这满足访问控制条目 (Allow, 'group:editors', 'edit') , which grants the edit 许可。因此,此路由匹配,并且控制传递到视图可调用 edit_page .

  4. edit_pagename 设置为 'FrontPage' ,页面名称来自 request.matchdict['pagename']page 设置为的实例 models.Page 它包含了 FrontPage .

  5. 因为这个请求不是来自表单, request.params 没有密钥 'form.submitted' .

  6. 这个 edit_page 函数调用 pyramid.security.authenticated_userid() logged_in is set to the userid 'editor' .

  7. 这个 edit_page 函数返回此字典:

    {'page':page, 'logged_in':'editor',
     'save_url':'http://localhost:6543/FrontPage/edit_page'}
    
  8. 模板 edit.pt 用这些值呈现。除此模板的其他功能外,这些行导致包含 Logout 链接:

    <span tal:condition="logged_in">
      <a href="${request.application_url}/logout">Logout</a>
    </span>
    

    对于示例案例,此链接将引用 http://localhost:6543/logout .

    模板的这些行以窗体的形式显示当前页的内容, action 属性是 http://localhost:6543/FrontPage/edit_page ::

    <form action="${save_url}" method="post">
      <textarea name="body" tal:content="page.data" rows="10" cols="60"/>
      <input type="submit" name="form.submitted" value="Save"/>
    </form>
    

循环4:

  1. 用户编辑页面内容并单击 Save .

  2. URL http://localhost:6543/FrontPage/edit_page 和以前一样,一直走到检查 request.params 有键 'form.submitted' . 这次,在 edit_page 查看可调用,执行以下行:

    page.data = request.params['body']
    session.add(page)
    return HTTPFound(location = route_url('view_page', request,
                                          pagename=name))
    

    前两行将旧页内容替换为 body 窗体中的文本区域,然后更新存储在数据库中的页。第三行导致响应,将浏览器重定向到 http://localhost:6543/FrontPage .

验证后重新访问

在这种情况下,用户在其浏览器中设置了一个身份验证cookie,将其登录名指定为 'editor' . 请求的URL是 http://localhost:6543/FrontPage/edit_page .

This process requires two request/response cycles.

  1. 用户点击 Edit page . 可调用的视图是 views.edit_page . 反应是 edit.pt ,显示当前页面内容。

  2. The user edits the content and clicks Save . 可调用的视图是 views.edit_page . 响应是重定向到 /Frontpage .

循环1:

  1. 有图案的路线 /{{pagename}}/edit_page 与URL匹配,由于身份验证cookie, groupfinder 返回包含 group:editors 校长,其中 models.RootFactory.__acl__ uses to grant the edit 权限,因此此路由匹配并发送到可调用的视图 views.edit_page() .

  2. edit_page ,因为请求不是来自表单提交, request.params 没有键 'form.submitted' .

  3. 变量 logged_in 设置为登录名 'editor' 通过呼叫 authenticated_userid 从身份验证cookie中提取。

  4. 函数返回此字典:

    {'page':page,
     'save_url':'http://localhost:6543/FrontPage/edit_page',
     'logged_in':'editor'}
    
  5. 模板 edit.pt 使用该字典中的值呈现。因为存在 'logged_in' A条目 Logout 链接出现。

循环2:

  1. 用户编辑页面内容并单击 Save .

  2. 这个 POST operation works as in Successful login .

注销

This process starts with a request URL http://localhost:6543/logout .

  1. 有图案的路线 '/logout' matches and dispatches to the view callable logout 在里面 login.py .

  2. 呼唤 pyramid.security.forget() 返回一个头元组列表,当响应返回时,该列表将导致浏览器删除用户的身份验证cookie。

  3. View Callable返回 HTTPFound 将浏览器重定向到命名路由的异常 view_wiki ,将转换为URL http://localhost:6543 . 它还传递删除认证cookie的头。