C# 无任何用户界面的Web API中基于令牌的身份验证
我正在ASP.NETWebAPI中开发RESTAPI。只能通过非基于浏览器的客户端访问我的API。我需要为我的API实现安全性,所以我决定使用基于令牌的身份验证。我对基于令牌的身份验证有相当的了解,并阅读了一些教程,但它们都有一些用于登录的用户界面。我不需要任何用户界面来登录,因为登录详细信息将由客户端通过HTTP POST传递,HTTP POST将由我们的数据库授权。如何在API中实现基于令牌的身份验证?请注意-我的API将以高频率访问,因此我还必须注意性能。C# 无任何用户界面的Web API中基于令牌的身份验证,c#,.net,authentication,asp.net-web-api,http-token-authentication,C#,.net,Authentication,Asp.net Web Api,Http Token Authentication,我正在ASP.NETWebAPI中开发RESTAPI。只能通过非基于浏览器的客户端访问我的API。我需要为我的API实现安全性,所以我决定使用基于令牌的身份验证。我对基于令牌的身份验证有相当的了解,并阅读了一些教程,但它们都有一些用于登录的用户界面。我不需要任何用户界面来登录,因为登录详细信息将由客户端通过HTTP POST传递,HTTP POST将由我们的数据库授权。如何在API中实现基于令牌的身份验证?请注意-我的API将以高频率访问,因此我还必须注意性能。 请让我知道我是否可以解释得更好。
请让我知道我是否可以解释得更好。ASP.Net Web API已经内置了授权服务器。当您使用Web API模板创建新的ASP.Net Web应用程序时,可以在Startup.cs中看到它 您所要做的就是在查询字符串中发布URL编码的用户名和密码
/Token/userName=johndoe%40example.com&password=1234&grant_type=password
如果你想知道更多细节,你可以观看
.我认为MVC和Web Api之间的区别有些混淆。简而言之,对于MVC,您可以使用登录表单并使用cookie创建会话。对于Web Api,没有会话。这就是为什么你想使用代币 您不需要登录表单。您只需要令牌端点。如文中所述,您将把凭证发送到处理凭证的令牌端点 下面是一些客户端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;
}
}
现在,服务器端:
在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);
在ApplicationAuthProvider.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);
}
}
//使用Microsoft.AspNet.Identity.Owin;
//使用Microsoft.Owin.Security;
//使用Microsoft.Owin.Security.OAuth;
//使用制度;
//使用System.Collections.Generic;
//使用System.Security.Claims;
//使用System.Threading.Tasks;
公共类ApplicationAuthProvider:OAuthAuthorizationServerProvider
{
私有只读字符串_publicClientId;
公共ApplicationAuthProvider(字符串publicClientId)
{
if(publicClientId==null)
抛出新ArgumentNullException(“publicClientId”);
_publicClientId=publicClientId;
}
公共重写异步任务GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentials上下文)
{
var userManager=context.OwinContext.GetUserManager();
var user=await userManager.FindAsync(context.UserName,context.Password);
if(user==null)
{
SetError(“无效的授权”,“用户名或密码不正确”);
返回;
}
ClaimsIdentity oAuthIdentity=等待用户.GenerateUserIdentityAsync(userManager);
var propertyDictionary=新字典{{“userName”,user.userName};
var属性=新的AuthenticationProperties(propertyDictionary);
AuthenticationTicket=新的AuthenticationTicket(OAuthidentitity,属性);
//令牌已验证。
上下文。已验证(票证);
}
公共重写任务令牌端点(OAuthTokenEndpointContext)
{
foreach(context.Properties.Dictionary中的KeyValuePair属性)
{
AdditionalResponseParameters.Add(property.Key,property.Value);
}
返回Task.FromResult(空);
}
公共覆盖任务ValidateClientAuthentication(OAuthValidateClientAuthenticationContext)
{
//资源所有者密码凭据不提供客户端ID。
if(context.ClientId==null)
context.Validated();
返回Task.FromResult(空);
}
公共覆盖任务ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext上下文)
{
if(context.ClientId==\u publicClientId)
{
var expectedRootUri=新Uri(context.Request.Uri,“/”;
if(expectedRootUri.AbsoluteUri==context.RedirectUri)
context.Validated();
}
返回Task.FromResult(空);
}
}
如您所见,检索令牌时没有涉及控制器。事实上,如果您只想要一个Web Api,您可以删除所有MVC引用。我简化了服务器端代码,使其更具可读性。您可以添加代码来升级安全性
确保只使用SSL。实现requireHttpAttribute以强制执行此操作
您可以使用Authorize/AllowAnonymous属性来保护Web Api。此外,您还可以添加过滤器(如RequireHttpAttribute)以使Web Api更加安全。我希望这能有所帮助。有些人,在某个地方必须输入用户名和密码才能进行初始验证;你是否建议获得你的应用程序副本的任何人使用相同的用户名和密码?如果是这样的话,您是否打算在代码中硬编码用户名和密码值?我可以有多个注册用户,因此初始登录详细信息将由他们通过HTTP POST传递。下一步是什么?那没有任何意义。如何将服务器凭据传递给客户端?服务器如何知道哪个客户端是哪个客户端?您必须以编程方式进行身份验证,以不需要用户界面的首选方式向身份验证服务传递凭据,这将向您传递回令牌。然后,您可以像往常一样使用此令牌拨打电话。@Claies抱歉造成混淆。其想法是让客户端传递登录详细信息,然后我的API生成一个令牌。这是否可行?请让我知道是否还有其他方法。因此,我将在HTTP头/正文中创建一个对/TOKEN的POST请求,其中包含用户名和密码?我将为我的应用程序数据库中的所有用户提供用户名和哈希密码。我应该如何实现这一点?您需要ASP.Net标识(我相信您已经有了)。如果没有,请创建一个ASP.Net Web API项目并查看源代码。什么是grant_type=password
//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; }
}
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);
//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);
}
}