Web API 中基于令牌的身份验证,没有任何用户界面
Token based authentication in Web API without any user interface
我正在 ASP.Net Web API 中开发 REST API。我的 API 只能通过非浏览器客户端访问。我需要为我的 API 实施安全性,所以我决定使用基于令牌的身份验证。我对基于令牌的身份验证有一定的了解,并且阅读了一些教程,但它们都有一些用于登录的用户界面。我不需要任何 UI 登录,因为登录详细信息将由客户端通过 HTTP POST 传递,这将从我们的数据库中获得授权。如何在我的 API 中实施基于令牌的身份验证?请注意-我的 API 将被高频访问,因此我还必须注意性能。
如果我能解释得更好,请告诉我。
ASP.Net Web API 已经内置了授权服务器。 当您使用 Web API 模板创建新的 ASP.Net Web 应用程序时,您可以在 Startup.cs 中看到它。
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
您所要做的就是在查询字符串中 post URL 编码用户名和密码。
/Token/userName=johndoe%40example.com&password=1234&grant_type=password
如果想了解更多详情,可以观看
User Registration and Login - Angular Front to Back with Web API by Deborah Kurata.
我认为 MVC 和 Web 之间的区别存在一些混淆 Api。简而言之,对于 MVC,您可以使用登录表单并使用 cookie 创建 session。对于 Web Api,没有 session。这就是您要使用令牌的原因。
您不需要登录表单。令牌端点就是您所需要的。就像 Win 描述的那样,您会将凭据发送到处理它的令牌端点。
下面是一些用于获取令牌的客户端 C# 代码:
//using System;
//using System.Collections.Generic;
//using System.Net;
//using System.Net.Http;
//string token = GetToken("https://localhost:<port>/", userName, password);
static string GetToken(string url, string userName, string password) {
var pairs = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>( "grant_type", "password" ),
new KeyValuePair<string, string>( "username", userName ),
new KeyValuePair<string, string> ( "Password", password )
};
var content = new FormUrlEncodedContent(pairs);
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
using (var client = new HttpClient()) {
var response = client.PostAsync(url + "Token", content).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
为了使用令牌将其添加到请求的 header:
//using System;
//using System.Collections.Generic;
//using System.Net;
//using System.Net.Http;
//var result = CallApi("https://localhost:<port>/something", token);
static string CallApi(string url, string token) {
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
using (var client = new HttpClient()) {
if (!string.IsNullOrWhiteSpace(token)) {
var t = JsonConvert.DeserializeObject<Token>(token);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.access_token);
}
var response = client.GetAsync(url).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
Token在哪里:
//using Newtonsoft.Json;
class Token
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public string userName { get; set; }
[JsonProperty(".issued")]
public string issued { get; set; }
[JsonProperty(".expires")]
public string expires { get; set; }
}
服务器端现在:
在Startup.Auth.cs
var oAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider("self"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// https
AllowInsecureHttp = false
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(oAuthOptions);
并且在 ApplicationOAuthProvider.cs 中实际授予或拒绝访问权限的代码:
//using Microsoft.AspNet.Identity.Owin;
//using Microsoft.Owin.Security;
//using Microsoft.Owin.Security.OAuth;
//using System;
//using System.Collections.Generic;
//using System.Security.Claims;
//using System.Threading.Tasks;
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
throw new ArgumentNullException("publicClientId");
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager);
var propertyDictionary = new Dictionary<string, string> { { "userName", user.UserName } };
var properties = new AuthenticationProperties(propertyDictionary);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
// Token is validated.
context.Validated(ticket);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
context.Validated();
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
var expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
context.Validated();
}
return Task.FromResult<object>(null);
}
}
如您所见,没有控制器参与检索令牌。事实上,如果您只需要 Web Api,则可以删除所有 MVC 引用。我简化了服务器端代码以使其更具可读性。您可以添加代码来升级安全性。
确保只使用 SSL。实施 RequireHttpsAttribute 以强制执行此操作。
您可以使用 Authorize / AllowAnonymous 属性来保护您的 Web Api。此外,您可以添加过滤器(如 RequireHttpsAttribute)以使您的 Web Api 更加安全。希望对您有所帮助。
我正在 ASP.Net Web API 中开发 REST API。我的 API 只能通过非浏览器客户端访问。我需要为我的 API 实施安全性,所以我决定使用基于令牌的身份验证。我对基于令牌的身份验证有一定的了解,并且阅读了一些教程,但它们都有一些用于登录的用户界面。我不需要任何 UI 登录,因为登录详细信息将由客户端通过 HTTP POST 传递,这将从我们的数据库中获得授权。如何在我的 API 中实施基于令牌的身份验证?请注意-我的 API 将被高频访问,因此我还必须注意性能。 如果我能解释得更好,请告诉我。
ASP.Net Web API 已经内置了授权服务器。 当您使用 Web API 模板创建新的 ASP.Net Web 应用程序时,您可以在 Startup.cs 中看到它。
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
您所要做的就是在查询字符串中 post URL 编码用户名和密码。
/Token/userName=johndoe%40example.com&password=1234&grant_type=password
如果想了解更多详情,可以观看 User Registration and Login - Angular Front to Back with Web API by Deborah Kurata.
我认为 MVC 和 Web 之间的区别存在一些混淆 Api。简而言之,对于 MVC,您可以使用登录表单并使用 cookie 创建 session。对于 Web Api,没有 session。这就是您要使用令牌的原因。
您不需要登录表单。令牌端点就是您所需要的。就像 Win 描述的那样,您会将凭据发送到处理它的令牌端点。
下面是一些用于获取令牌的客户端 C# 代码:
//using System;
//using System.Collections.Generic;
//using System.Net;
//using System.Net.Http;
//string token = GetToken("https://localhost:<port>/", userName, password);
static string GetToken(string url, string userName, string password) {
var pairs = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>( "grant_type", "password" ),
new KeyValuePair<string, string>( "username", userName ),
new KeyValuePair<string, string> ( "Password", password )
};
var content = new FormUrlEncodedContent(pairs);
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
using (var client = new HttpClient()) {
var response = client.PostAsync(url + "Token", content).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
为了使用令牌将其添加到请求的 header:
//using System;
//using System.Collections.Generic;
//using System.Net;
//using System.Net.Http;
//var result = CallApi("https://localhost:<port>/something", token);
static string CallApi(string url, string token) {
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
using (var client = new HttpClient()) {
if (!string.IsNullOrWhiteSpace(token)) {
var t = JsonConvert.DeserializeObject<Token>(token);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.access_token);
}
var response = client.GetAsync(url).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
Token在哪里:
//using Newtonsoft.Json;
class Token
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public string userName { get; set; }
[JsonProperty(".issued")]
public string issued { get; set; }
[JsonProperty(".expires")]
public string expires { get; set; }
}
服务器端现在:
在Startup.Auth.cs
var oAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider("self"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// https
AllowInsecureHttp = false
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(oAuthOptions);
并且在 ApplicationOAuthProvider.cs 中实际授予或拒绝访问权限的代码:
//using Microsoft.AspNet.Identity.Owin;
//using Microsoft.Owin.Security;
//using Microsoft.Owin.Security.OAuth;
//using System;
//using System.Collections.Generic;
//using System.Security.Claims;
//using System.Threading.Tasks;
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
throw new ArgumentNullException("publicClientId");
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager);
var propertyDictionary = new Dictionary<string, string> { { "userName", user.UserName } };
var properties = new AuthenticationProperties(propertyDictionary);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
// Token is validated.
context.Validated(ticket);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
context.Validated();
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
var expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
context.Validated();
}
return Task.FromResult<object>(null);
}
}
如您所见,没有控制器参与检索令牌。事实上,如果您只需要 Web Api,则可以删除所有 MVC 引用。我简化了服务器端代码以使其更具可读性。您可以添加代码来升级安全性。
确保只使用 SSL。实施 RequireHttpsAttribute 以强制执行此操作。
您可以使用 Authorize / AllowAnonymous 属性来保护您的 Web Api。此外,您可以添加过滤器(如 RequireHttpsAttribute)以使您的 Web Api 更加安全。希望对您有所帮助。