我正在尝试设计我的第一个公共API,并且我正在尝试学习REST如何使用身份验证,尤其是在使用js-frameworks的完全客户端应用程序的上下文中,例如angularJS。
假设您有一个客户端,它是一个浏览器应用程序(即,仅限HTML,JS,CSS),用作nginx之类的静态文件,使用javascript框架来使用REST服务,例如:需要秘密访问密钥的东西,用于为每个服务请求创建签名,如Amazon S3。
在这种情况下的身份验证方面,如果您没有服务器端应用程序,那么如何处理秘密访问密钥,即如何获取密钥,在何处存储密码等等?为每个请求提供密钥似乎是一种可怕的安全状况(即使它只发生一次以引导应用程序)。
即使你有一个轻便的服务器端应用程序 - 你如何安全地通知客户端(仍然调用经过身份验证的第三方API本身)签名对于它可能做出的每个请求应该是什么?对于如何从两端进行设计,我感到非常困惑。
答案 0 :(得分:1)
我打算写一个很长的答案,但我认为@Arjan涵盖了Stack Overflow post中的所有内容。他和他的团队推出了自己的解决方案,但他解决了REST身份验证中的关键问题。当然,根据您的具体情况,您可以使用OAuth,OpenID或SAML等内容。 Here是对三种方法的比较。
注意秘密和密钥之间的区别。另请注意,每个请求都需要发送令牌,因为REST是无状态的。
答案 1 :(得分:0)
我已经完成了一些AngularJS应用程序,我发现的方法是使用像这样的HttpModule
:
using System;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web;
namespace YourSolution.WebApp.Modules
{
public class BasicAuthenticationHttpModule : IHttpModule
{
public BasicAuthenticationHttpModule()
{
}
public void Init(HttpApplication context)
{
context.AuthenticateRequest += OnApplicationAuthenticateRequest;
context.EndRequest += OnApplicationEndRequest;
}
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
private static bool CheckPassword(
string username, string password)
{
return username == password;
}
private static void AuthenticateUser(string credentials)
{
try
{
var encoding = Encoding.GetEncoding(
"iso-8859-1");
credentials = encoding.GetString(
Convert.FromBase64String(credentials));
var separator = credentials.IndexOf(':');
var name = credentials.Substring(0, separator);
var password = credentials.Substring(separator + 1);
var validated = CheckPassword(name, password);
if (!validated) return;
var identity = new GenericIdentity(name);
SetPrincipal(new GenericPrincipal(identity, null));
}
catch (FormatException)
{
}
}
private static void OnApplicationAuthenticateRequest(
object sender, EventArgs e)
{
var request = HttpContext.Current.Request;
var authHeader = request.Headers["Authorization"];
if (authHeader == null) return;
var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);
if (authHeaderVal.Scheme.Equals(
"basic",
StringComparison.OrdinalIgnoreCase)
&& authHeaderVal.Parameter != null)
{
AuthenticateUser(authHeaderVal.Parameter);
}
}
private static void OnApplicationEndRequest(
object sender, EventArgs e)
{
var response = HttpContext.Current.Response;
if (response.StatusCode == 401)
{
//response.Headers.Add(
// "WWW-Authenticate",
// string.Format("Basic realm=\"{0}\"", Realm));
}
}
public void Dispose()
{
}
}
}
最重要的部分是CheckPassword
方法,您应该在哪里验证凭据。
另一点是这一行response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", Realm));
如果您不对此行进行评论,则会显示经典登录请求表单,如果您对此行发表评论,则必须在此处捕获401
错误你的要求。
如果你想了解领域:What is the “realm” in basic authentication。
另外,您需要在web.config文件中注册该模块:
<system.webServer>
<modules>
<add
name="BasicAuthenticationHttpModule"
type="YourSolution.WebApp.Modules.BasicAuthenticationHttpModule" />
</modules>
</system.webServer>
然后我添加了这两种方法来处理身份验证令牌:
// u: username; p: password
CreateBasicAuthenticationToken = function (u, p) {
var t = u + ':' + p;
var hat = btoa(t);
window.sessionStorage.setItem('basicauthtoken', 'basic ' + hat);
};
DestroyBasicAuthenticationToken = function () {
window.sessionStorage.removeItem('basicauthtoken');
};
btoa
方法:The btoa() method of window object is used to convert a given string to a encoded data (using base-64 encoding) string.
。取自:http://www.w3resource.com/javascript/client-object-property-method/window-btoa.php。
最后我使用beforeSend
将authtoken添加到请求标头中:
$.ajax({
type: 'GET',
url: 'your url',
beforeSend: function (xhr) {
window.sessionStorage.getItem('basicauthtoken');
}
}).done(function (data, textStatus, xhr) {
//...
});
请注意不建议在angular指令之外使用jQuery,AngularJS最佳实践规定jQuery代码必须始终放在指令内。
希望它有所帮助。