我正在ASP.Net Web API中开发REST API。我的API仅可通过非基于浏览器的客户端访问。我需要为我的API实现安全性,因此我决定使用基于令牌的身份验证。我对基于令牌的身份验证有一定的了解,并且阅读了一些教程,但是它们都具有一些用于登录的用户界面。我不需要任何UI进行登录,因为登录详细信息将由客户端通过HTTP POST传递,而HTTP POST将从我们的数据库中获得授权。如何在我的API中实现基于令牌的身份验证?请注意- 我的API将以高频率访问,因此我也必须注意性能。请让我知道是否可以更好地解释它。
我认为MVC和Web Api之间的区别有些混乱。简而言之,对于MVC,您可以使用登录表单并使用Cookie创建会话。对于Web Api,没有会话。这就是为什么要使用令牌。
您不需要登录表单。令牌端点就是您所需要的。就像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; } }
为了使用令牌,将其添加到请求的标头中:
//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; } }
令牌在哪里:
//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更安全。我希望这有帮助。