(我不是在谈论验证对RESTful API的调用。我在谈论通过RESTful API为用户创建登录逻辑。)
当用户访问我网站的任何页面时,servlet filter
会拦截该请求,并在authentication info
中检查是否存在session
。如果不存在,将指示用户登录页面。
在登录页面上,使用用户名和密码对服务器上的RESTful API进行ajax调用。根据该RESTful API的返回状态,页面上的JavaScript将决定是否允许用户访问该站点。请注意,实际的身份验证逻辑仍在服务器端执行。客户端JS仅根据服务器的结果执行操作。
在服务器上,RESTful登录API将检查提交的用户名/密码,并查看它是否包含在DB中。如果存在,它会将必要的authentication info
存储到session
中,以便不会阻止来自同一客户端的未来请求。
我的问题是:
此登录逻辑是否正常?
由于涉及session
,因此RESTful登录API有点not stateless
。它仍然是RESTful吗?
这是我的代码:
login.js
// login.js
$(function () {
$('#submitDiv').click(doLogin);
});
function doLogin() {
$('#resultDiv').text("start!");
user = new Object();
user.username = $('#txtUsername').val();
user.pwd = $('#txtPassword').val();
$.ajax({
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
'type': 'POST',
'url': 'doLogin.sm',
'data': JSON.stringify(user),
'dataType': 'text',
'success': loginSuccessful,
'complete': function (jqXHR, textStatus) {
$('#resultDiv').text("complete with:" + textStatus);
}
});
}
function loginSuccessful() {
//if referrer is null, jump to dashboard, else jump to referrer.
var referer = getUrlVars()['referer'];
if (referer) {
window.location.replace(referer);
}
else {
window.location.replace('dashboard.html');
}
}
的login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Cloud - Login page</title>
<link rel="stylesheet" type="text/css"
href="resources2/css/bootstrap.css">
<meta charset="utf-8">
<!--force to use the latest IE engine-->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="resources2/js/jquery-1.11.3.js"></script>
<script src="resources2/js/bootstrap.js"></script>
<script src="resources2/js/pagejs/common.js"></script>
<script src="resources2/js/pagejs/login.js"></script>
</head>
<body>
<h1>My Login Page</h1>
<div id="loginDiv">
<input id="txtUsername" type="text" value="test">
<input id="txtPassword" type="password" value="test">
<div id="submitDiv" class="btn btn-default">
Login
</div>
<div id="resultDiv"></div>
</div>
</body>
</html>
答案 0 :(得分:1)
如果您的JavaScript决定是否允许用户从您的网站获取网页,那么是什么阻止用户在JavaScript代码中设置断点并更改身份验证过程的结果?
授权决策需要在服务器上进行,因为您永远不能信任客户端(浏览器)。
您的站点需要在服务器端验证用户是否已通过身份验证。这通常通过发送某种implicit authentication信息以及服务器可以验证且客户端无法伪造的请求来完成。
我在设计中看不到这种情况。但为什么要尝试设计自己的身份验证机制?为什么不使用WS-Federation,SAML-P或OpenID Connect这样的标准协议?
答案 1 :(得分:1)
这就是Roy Thomas Fielding, The REST Godfather ,在dissertation stateless constraint中说Authorization
:
5.1.3无国籍
[...]从客户端到服务器的每个请求必须包含理解请求所需的所有信息,并且不能利用服务器上任何存储的上下文。 会话状态因此完全保留在客户端上。 [...]
因此,如果您在服务器上保持会话状态,那么它不是REST。
在REST中,您将无法在服务器上进行会话,因此您将没有会话标识符。
从客户端到服务器的每个请求都必须包含服务器要理解的所有必要信息。有了它,您不依赖于存储在服务器上的任何会话上下文。
例如,在访问需要身份验证的受保护资源时,每个请求必须包含所有必要的数据才能进行正确的身份验证/授权。 表示将对每个请求执行身份验证。
身份验证数据应属于标准HTTP RFC 7235标头。来自RFC 7617:
<强> 4.2。授权
Authorization
标头字段允许用户代理进行身份验证 本身与原始服务器 - 通常,但不一定,后 收到401
(未经授权)回复。它的价值包括 包含用户身份验证信息的凭据 被请求资源领域的代理。 [...]
Base64中定义的基本身份验证方案是保护REST API的良好开端:
<强> 2。 “基本”认证方案
基本身份验证方案基于客户端的模型 需要使用每个用户ID和密码进行身份验证 保护空间(“领域”)。 [...]服务器只有在可以验证时才会为请求提供服务 应用于保护空间的用户标识和密码 请求的资源。
[...]
要获得授权,客户
- 获取用户ID和密码
从用户
通过连接user-id(单个)来构造用户传递 冒号(“:”)字符和密码
将用户传递编码为八位字节序列,
- 的序列 醇>
并通过编码此八位字节序列来获取基本凭证 将US-ASCII characters用于man-in-the-middle attack。
[...]
如果用户代理希望发送用户ID“Aladdin”和密码 “打开芝麻”,它将使用以下标题字段:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
[...]
请记住 HTTPS 是阻止RFC 7519的最好朋友。
如果您不希望为每个请求通过网络发送用户名和密码,则可以考虑创建基于令牌的身份验证。在此方法中,您将为每个请求中发送的令牌交换您的硬凭证(用户名和密码)。
同样,必须为每个请求执行身份验证。
基本上,令牌可以是 opaque (除了值本身之外没有显示任何细节,比如随机字符串)或者可以自包含< / em>(例如 JSON Web Token )。
随机字符串:可以通过生成随机字符串并将其持久保存到具有过期日期且与其关联的用户标识符的数据库来发出令牌。
< / LI>JSON Web令牌(JWT):由JSON定义,它是在两方之间安全地表示声明的标准方法。 JWT是一个自包含令牌,使您能够在有效负载中存储用户标识符,到期日期和任何您想要的内容(但不存储密码),这是Base64编码为http://jwt.io。客户端可以读取有效负载,并且可以通过验证服务器上的签名来轻松检查令牌的完整性。如果您不需要跟踪JWT令牌,则无需持久保存JWT令牌。尽管如此,通过持久存在令牌,您将有可能使其无效并撤销其访问权限。要找到一些与JWT合作的优秀资源,请查看many databases。
有relational databases您可以持久保存令牌。根据您的要求,您可以探索不同的解决方案,例如key-value stores,document stores或{{3}}。