11

这个问题是关于一般网络应用程序登录流程的问题。我对在保持安全性的同时优化可用性和性能的答案最感兴趣。

处理对书签 URL 的未经身份验证请求的最合适方法是什么?

为了演示该问题,以下是示例应用程序的一些路由和各自的行为:

GET /login         -> Display the authentication form
POST /processLogin -> process the username and password, 
                            if unauthentic...re-render the login form; 
                            otherwise...display the default page
GET /secret         -> if authenticated...display the secret resource;
                       otherwise...display a login form
POST /secret        -> if authenticated...perform a desirable, but potentially 
                                          non-idempotent action on the secret 
                                          resource
                       otherwise...display a login form

选项 1:显示登录屏幕,重定向到所需页面

  1. 用户点击书签
  2. GET /secret -> 200, 偷偷显示带有隐藏字段path="/secret"的登录表单
  3. POST /processLogin -> 302 到 /secret(路径参数的值)
  4. GET /secret -> 200,显示秘密资源

分析:希望您的客户端是现代浏览器,不兼容 HTTP,因此它在 302 的 POST 之后执行 GET。这适用于所有方面。我应该担心吗?

选项 2:重定向到登录屏幕,重定向到所需页面

  1. 用户点击书签
  2. 获取 /secret -> 302 到 /login
  3. GET /login via redirect -> 200,登录表单显示隐藏字段path="/secret"
  4. POST /processLogin -> 302 到 /secret
  5. GET /secret -> 200,显示秘密资源

分析:和上面一样的问题。增加登录时浏览器显示的URL发生变化,给用户造成混淆,破坏书签、链接共享等问题。

选项3:显示登录屏幕,显示所需页面

  1. 用户点击书签
  2. GET /secret -> 200,偷偷地显示登录表单,操作为 =“/secret”
  3. POST /secret -> 200,显示秘密资源

分析:可悲的是,刷新按钮现在也坏了:刷新将导致用户代理重新发布并发出警告,而不是重新获取 /secret。他们的用户会收到警告,但如果他们忽略它,就会发生不好的事情。

从好的方面来说,您可以使用这种技术最大限度地减少往返次数。

选项 4:重定向到登录屏幕,显示所需页面

  1. 用户点击书签
  2. 获取 /secret -> 302 到 /processLogin
  3. 通过重定向获取 /processLogin -> 200,登录表单显示为action ="/secret"
  4. POST /secret -> 302 到 /secret
  5. GET /secret -> 200,显示秘密资源

分析:与选项 2+4 相同的问题。

选项5:???

我还缺少另一种技术吗?

一般来说,您会推荐哪些技术?

也可以看看

重定向到登录页面时正确的 HTTP 状态代码是什么? 什么样的 HTTP 重定向用于登录? 带有重定向的 HTTP 响应,但没有往返?

4

5 回答 5

5

选项 1 和 3 不遵循HTTP RFC,因为“秘密显示登录表单”与 200 GET 响应相矛盾,其中“响应中发送了与请求的资源相对应的实体”是预期的。

选项 2 没问题。所有现代浏览器都支持 POST 上的 302,许多基于 REST 的框架(如 RoR)都在积极使用它。或者,在“302 to /login”中,您已经可以创建会话(cookie)并将 URL 存储在会话中,以避免在 GET 参数中传递原始 URL。从可用性的角度来看,您也可以在登录页面上显示适当的消息(我认为 URL 不匹配在这里无关紧要 - 无论如何您不能让用户看到内容)。

选项 4:当您 POST 到 /secret 时,HTTP RFC 希望您“接受请求中包含的实体作为请求行中的请求 URI 标识的资源的新下级”,但您所做的只是记录在而不是在 /secret 下创建任何新内容。

因此,遵循 HTTP RFC,您最好的选择是Option 2。实际上,选项 2 也符合 POST->Redirect->GET 设计模式,这有助于解决将 URL 添加到 POST 资源的不可预测性问题。

于 2013-03-14T09:39:30.177 回答
3

我的 $.02:我最近使用选项 2 实现了(虽然我存储/secret在会话中,而不是作为隐藏字段存储在登录表单中)。

我并不完全同意您的担忧:

增加登录时浏览器显示的URL发生变化,给用户造成混淆,破坏书签、链接共享等问题。

重定向到/login以及随后的 URL 更改告诉用户,在他们可以继续之前,还需要先完成其他事情:登录。

由于登录页面看起来与“目标页面”完全不同,我看不出这会如何使人们混淆为书签和/或链接共享登录页面而不是目标页面(因为登录页面不包含他们想要收藏/分享的信息)。

如果您担心 302 会破坏标准(尽管我知道的每个浏览器都会很乐意破坏它),请考虑改用 303。

于 2013-03-09T15:23:34.970 回答
1

请注意,mickeyreiss 是正确的,使用 AJAX 选项 3 没有后退按钮损坏的缺点。但是,这意味着用户必须启用 JavaScript。话虽这么说,如果您正确编写表单,则可以检测 JS 是否存在,如果不使用选项 1。

请注意,302 响应很好,但是,您可能会遇到缓存问题。如果您想为同一个 URI 显示 2 个完全不同的页面/表单,您必须确保没有任何内容被缓存。(/secret 显示登录名,然后显示实际密码。)

于 2013-03-09T11:01:55.220 回答
1

我几乎总是使用选项 #2,仅仅是因为 URL 返回的内容是一致的。虽然今天的秘密隐藏在登录后面,但明天您可能想要打开它或根据同一 URL 的身份验证显示混合的公共/秘密。在这种情况下,选项 #2 将最符合 Google 的预期。谷歌看不起任何内容诱饵和切换,在极端情况下,您的所有页面都会有重复的页面内容(即登录表单)。

于 2013-03-14T19:09:10.660 回答
0

我会选择使用 AJAX 的选项:

  1. 登录页面并隐藏内容
  2. 用户输入登录名和密码。
  3. 身份验证在服务器端完成。
  4. 服务器返回结果
  5. 如果成功使用location.href设置您想去的页面,否则您可以输出一条消息说登录无效。
  6. 在您的服务器中,您将测试一个_SESSION变量,如果未设置重定向到登录页面..
于 2013-03-14T19:05:47.703 回答