我正在用ASP.NETWebAPI开发睡觉API.我的API将只能通过非基于浏览器的客户端访问.我需要为我的API实现安全性,所以我决定使用基于令牌的身份验证.我对基于令牌的身份验证有一定的了解,也读过一些教程,但它们都有一些用于登录的用户界面.我不需要任何用户界面登录,因为登录细节将由客户端通过HTTP POST传递,HTTP POST将从我们的数据库中授权.如何在API中实现基于令牌的身份验证?请注意-我的API将被访问的频率很高,所以我还必须注意性能. 如果我能解释得更清楚,请告诉我.

推荐答案

我认为MVC和WebAPI之间的区别有些混淆.简而言之,对于MVC,您可以使用登录表单并使用cookie创建会话.对于Web Api,没有会话.这就是为什么你想使用 token .

您不需要登录表单.您只需要令牌端点.就像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;
        }
    }

其中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; }
}

现在来看服务器端:

在初创阶段.啊.反恐精英

        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);
    }

}

正如您所见,在检索令牌时没有涉及控制器.事实上,如果您只想要一个Web Api,就可以删除所有MVC引用.我简化了服务器端代码,使其更具可读性.您可以添加代码来升级安全性.

确保您只使用SSL.实现RequireHttpsAttribute以强制执行此操作.

您可以使用Authorize/AllowAnonymous属性来保护Web Api.此外,您还可以添加过滤器(如RequireHttpAttribute),以使Web Api更安全.我希望这有帮助.

.net相关问答推荐

节省用户在整个应用程序中使用的Flutter

Msbuild try 构建 msbuild.exe 而不是我的 .csproj 文件

如何将 Assembly.CodeBase 转换为 C# 中的文件系统路径?

C#字符串的GetHashCode()是如何实现的?

如何在 FtpWebRequest 之前判断 FTP 上是否存在文件

在 web api 控制器(.net 核心)中使用 async/await 或任务

为什么 C# 不允许像 C++ 这样的非成员函数

具有透明背景且包含不透明控件的 WPF 窗口

(C# 7.2)私有保护修饰符的用例是什么?

简单委托(委托)与多播委托

如何使用c#从excel文件中读取数据

如何通过 LINQ 比较没有时间的 DateTime?

支持 HTTPS 的 Httplistener

C# 编译为 32/64 位,或任何 cpu?

VB.NET 与 C# 整数除法

C#As的 VB.NET 类似功能

风格上的差异:IDictionary vs Dictionary

如何从其十六进制 RGB 字符串创建 System.Drawing.Color?

了解 C# 中的协变和逆变接口

如何使用匿名方法返回值?