在Backbone.js应用中管理身份验证(会话)和授权的最佳设计/架构实践是什么?

9

我在任何地方都找不到太多关于这方面的信息。大多数示例应用程序都没有讨论安全性等问题。假设用户将使用基于REST的API调用进行身份验证。应该如何构建应用程序来处理身份验证[还包括授权]。提供一个示例应用程序的指针将是很好的。请考虑单页应用程序和普通应用程序的视图。 [我认为每个视图都应该负责它]


1
示例应用程序不涉及安全问题,因为很少有(如果有的话)与Backbone相关的内容需要讨论。 - mu is too short
只是为了更清楚,我对应用身份验证/授权模式而不损害安全性感兴趣。例如,我应该使用中介者模式来保存会话/授权信息,并且每个视图在呈现之前都应该检查此信息。还是应该通过其他方式处理这个问题? - Pradeep Kumar Mishra
我想到了一个解决方案。最好的方法是创建一个名为“authenticated route/view”的父路由和视图,并从中派生所有需要用户登录的路由/视图。在路由上添加安全性将不会实例化不必要的视图。会话安全性、授权参数等需要从服务器获取,并且每个请求也应该在服务器端进行验证以增加安全性。此外,还可以参考Anthony的答案来处理全局错误等问题。 - Pradeep Kumar Mishra
3个回答

8
您说得没错,大多数示例根本没有涉及安全问题。因此,我没有一个很好的示例可以向您指出。我只能告诉您我们如何构建我们自己的东西:
1. 像您建议的那样,我们通过API调用对用户进行身份验证。在我们的情况下,Java服务器会在用户第一次进入页面时(甚至在登录之前)创建一个会话ID,并将其存储在客户端的cookie中。由于该cookie随着每个HTTPS请求到达服务器(即使是获取数据或执行命令的AJAX调用),因此一旦用户进行身份验证,它就与特定账户服务器端相关联。
2. 假设您传输到服务器的内容是HTTPS且cookie从未经过公开互联网传输,则没有人可以窃听该值并冒充已登录的用户。
3. 在我们的情况下,服务器基于Java,一个servlet过滤器位于所有不公开访问的API函数之前(即需要登录的函数)。它检查会话并确保它表示已登录的用户,然后将请求传递给服务,并使服务代码免于身份验证检查。但是,授权代码和参数验证目前确实位于服务层。
4. 即使您随附有cookie,AJAX调用API也可能无法进行身份验证,原因有很多(会话已过期,服务器必须重新启动并已忘记用户的会话,管理员强制注销了用户等)。在我们看来,重要的是服务器仍然返回某些内容(即不是空响应),而且它不应该是像重定向到登录页面这样的东西(对于AJAX请求毫无用处)。因此,我们始终从所有函数返回JSend协议响应,如果用户未登录,则servlet过滤器将返回带有特定代码的标准JSend“error”响应。这使我们的客户端代码(您可以将其放入自定义Sync中)注意到用户未登录并提示登录。甚至可以在登录后自动重试该函数,但这比我们得到的更复杂。
5. 通过让同步程序注意到用户未登录或遇到安全违规,您无需在视图中添加任何特殊内容。他们只需发出他们认为合适的任何请求,它要么成功,要么失败。如果需要进行新的登录,则会在较低级别触发该操作。
6. 登出实际上不需要您终止本地cookie,只要服务器将给定会话标记为不再登录或丢弃会话记录即可。
我在德里克的说法上有些不同意,他认为客户端不需要知道安全性。我认为客户端需要知道何时提示登录,如何告诉服务器何时执行注销操作,而且我总是认为在客户端进行额外的检查以避免惊喜原则是一个好主意。例如,如果我不能使用管理员功能,我更希望客户端不显示它们,而不是允许我尝试调用它们,然后得到错误响应。
最终,服务器绝对必须在每个请求中再次检查用户权限(因为绕过客户端安全非常容易),但良好的用户体验要求客户端知道我不是管理员,以便为我呈现最佳的UI表示。

谢谢John,我明白了。请看一下我对Derick回复的评论,让我知道你的想法。 - Pradeep Kumar Mishra
这是一个很棒的答案。我一直在思考这些问题。谢谢@John。嘿,为什么强制在服务器上注销比仅仅清除本地cookie或会话重要?我想不出有什么优势。 - SimplGy
@SimpleAsCouldBe,我认为我们并不认为强制服务器退出很重要,这只是我们的注销方式。我们触发它,然后服务器知道我们已经注销了。接下来,任何尝试客户端操作的请求都将保证得到失败响应并提示进行新的登录。我不确定一旦你删除了客户端cookie,如果其他人可以访问该机器或浏览器是否有办法恢复它并访问会话服务器端。 - John Munsch

5
在几个月前,我做了一个消耗REST API的单页应用程序时遇到了同样的问题。在寻找答案后,我想到了使用HTTP现有的401和403错误。我让我的API返回这些错误。然后,通过使用扩展的错误处理模型来捕获异常,处理这些错误并将它们路由到我的登录页面,使用路由器navigate函数。
var ErrorHandlerModel = Backbone.Model.extend({

    initialize: function(attributes, options) {
        options || (options = {});
        this.on("error", this.errorHandler);
        this.init && this.init(attributes, options);
    },

    errorHandler: function(model, error) {
        if (error.status == 401 || error.status == 403) {
          app.history.navigate('login', true);
        }
    }

});

回顾过去,我认为最好只是使用全局的jquery ajaxError 函数。上面的片段是基于几个月前发布的一个类似的问题。我还不得不覆盖backbone的默认获取行为,以便我可以通过ogin触发错误来捕获api的json响应中包含的响应变量。
var Login = Backbone.Model.extend({  

    urlRoot: '/login',

    parse: function(resp,xhr) {
        if (resp.response == 'success') {
            app.history.navigate('dashboard', true);
        }
        else {
            this.trigger('loginError');     
        }
    return false;
    }
});

这听起来不错。你们把认证检查放在哪里了?是在视图中还是在控制器中,例如,假设用户在登录后登陆到仪表板,现在你是在控制器实例化还是在视图中放置认证代码。我认为在视图中放置授权检查是有意义的。如果我没有表达清楚,请告诉我。 - Pradeep Kumar Mishra
1
如果您所说的控制器是路由器,那么是的,我们正在路由器上进行身份验证检查,因为我们有用户角色。同时,出于同样的原因,我们也在视图上进行身份验证检查,因为我们必须将某些事件限制为特定的用户角色。对于实际的用户数据,我们实际上使用登录模型来存储并检查其身份验证状态。这里只粘贴了一个简化版本。 - Anthony Chua
很酷。非常感谢。在关闭之前,您介意分享一些示例代码吗? - Pradeep Kumar Mishra
如果您要使用ajaxError处理程序,您会在全局处理程序中放置特定路由的逻辑吗?还是有更好的方法。我正在考虑身份验证问题返回401时,您想显示有关失败原因的特定消息,而不是通用身份验证错误。 - Nathan

1

我同意Mu的评论。在认证方面,没有太多可谈论的。

对于使用标准HTML应用程序向服务器提交数据的身份验证,您应该按照应该完成的方式进行处理。

在授权方面,有一些需要讨论的内容,但并不多。基本上,您只需向用户发送他们被允许查看的内容。除非绝对必要,否则不要在浏览器中执行授权或认证代码。根据我的经验,这从未是绝对必要的。

浏览器中的JavaScript不安全,因此不要在浏览器中执行依赖于身份验证和授权的操作。

我写了一篇关于这方面的小文章,链接在这里:http://lostechies.com/derickbailey/2012/01/26/modularity-and-security-in-composite-javascript-apps/


我部分地同意你的观点,即我们不应完全依赖JavaScript,但我的问题更具体,涉及到由backbone.js驱动的应用程序,特别是单页应用程序,其中视图、模型等都在客户端。现在,一旦我获得了用户已经通过身份验证(并且已授权)的令牌,我应该如何使用该令牌?我应该在视图中检查令牌,然后相应地呈现吗?另外,这样做足够安全吗? - Pradeep Kumar Mishra
你在这一点上相当安全。无论你是使用每个请求都发送的令牌还是像我们使用的 cookie,基本上你只是将令牌用作用户凭据的代理,并依赖服务器端使用该代理查找权限以进行检查。某些东西(无论是什么)需要随着每个请求一起发送,但仅此而已。我们在客户端下载了一组附加信息,其中包括可以在呈现时使用的权限标志,以抑制或显示接口元素,但这不是必需的。 - John Munsch
@Derick Bailey,我给你的回答投了反对票,唯一的原因是我没有看到任何回答被问到的问题。 - P.M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接