【问题标题】:IdentityServer4 API Key SupportIdentityServer4 API 密钥支持
【发布时间】:2017-07-21 13:02:23
【问题描述】:

我在 Visual Studio 中有 3 个项目。

  1. 身份服务器
  2. API 站点
  3. Angular 2 前端

我将 IdentityServer4 与 ASP.NET Core 身份和实体框架一起使用。到目前为止一切都很好。

现在,我想为用户添加生成 API 密钥的功能,以便用户可以通过他们的服务器调用我们的 API 服务器并简单地传递 API 密钥。然后(以某种方式)对用户进行身份验证并生成我的 API 用于授权的访问令牌。 IdentityServer4 是否支持为每个用户生成 API 密钥?我知道有客户端机密,我已经实施了但仅识别客户端。我也需要了解用户,这样我才能知道用户被授权做什么(现在使用角色)。

【问题讨论】:

  • 每个用户的 API 密钥和用户密码有什么区别?
  • 好问题。在 API 调用中无需用户名/密码即可使用 API 密钥。我研究这个只是为了向外部开发人员提供类似于使用 Google API、Swipe、StormPath 和许多其他 API 提供程序的体验。
  • 这方面有什么更新吗? IdentityServer4 是否支持 Api Keys?
  • 这有什么更新吗?接受的答案没有回答@206mph 提到的问题。

标签: asp.net-core api-key identityserver4


【解决方案1】:

IdentityServer 是一个框架和可托管组件,它允许 为现代网络实施单点登录和访问控制 使用 OpenID Connect 和 OAuth2 等协议的应用程序和 API。 它支持广泛的客户端,如移动、Web、SPA 和桌面 应用程序,并且是可扩展的,以允许在新的集成和 现有架构。 在你的创业课程中:

     public void ConfigureServices(IServiceCollection services)
                {
                    var source = System.IO.File.ReadAllText("MyCertificate.b64cert");
                    var certBytes = Convert.FromBase64String(source);
                    var certificate = new X509Certificate2(certBytes, "password");

                    var builder = services.AddIdentityServer(options =>
                    {
                        options.SigningCertificate = certificate;
                        options.RequireSsl = false; // should be true
                    });
             JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        app.UseIdentityServerAuthentication(options =>
        {
            options.Authority = "http://localhost:5000";
            options.ScopeName = "openid";
            options.AutomaticAuthenticate = true;
            options.AutomaticChallenge = true;
        });
                    builder.AddInMemoryClients(Clients.Get());
                    builder.AddInMemoryScopes(Scopes.Get());
                    builder.AddInMemoryUsers(Users.Get());
                }

            public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
                    {
                        loggerFactory.AddConsole(LogLevel.Verbose);
                        loggerFactory.AddDebug(LogLevel.Verbose);
                        app.UseIdentityServer();
                    }

创建一个客户端类

public class Clients
{
    public static IEnumerable<Client> Get()
    {
        return new[]
        {
            new Client
            {
                ClientId = "myapi",
                ClientSecrets = new List<Secret>
                {
                    new Secret("secret".Sha256())
                },
                ClientName = "your api",
                Flow = Flows.ResourceOwner,
                AllowedScopes =
                {
                    Constants.StandardScopes.OpenId,
                    "read"
                },
                Enabled = true
            }
        };
    }
}

范围

public class Scopes
{
    public static IEnumerable<Scope> Get()
    {
        return new[]
        {
            StandardScopes.OpenId,
            StandardScopes.Profile,
            StandardScopes.OfflineAccess,
            new Scope {Name = "advanced", DisplayName = "Advanced Options"}
        };
    }
}

控制器

[Route("api/[controller]")]
public class SomeController : Controller
{
    [HttpGet]
    [Authorize]
    public IEnumerable<string> Get()
    {
        return new[] { "value1", "value2" };
    }

    [HttpGet("{id}")]
    [Authorize]
    public string Get(int id)
    {
        return "value";
    }
}

设置完成后,您可以发出 JWT 身份验证令牌,然后用户可以在其授权标头中使用该令牌

创建一个生成类

private async Task GenerateToken(HttpContext context)
{
    var username = context.Request.Form["username"];
    var password = context.Request.Form["password"];

    var identity = await GetIdentity(username, password);
    if (identity == null)
    {
        context.Response.StatusCode = 400;
        await context.Response.WriteAsync("Invalid username or password.");
        return;
    }

    var now = DateTime.UtcNow;

    // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
    // You can add other claims here, if you want:
    var claims = new Claim[]
    {
        new Claim(JwtRegisteredClaimNames.Sub, username),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(JwtRegisteredClaimNames.Iat, now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
    };

    // Create the JWT and write it to a string
    var jwt = new JwtSecurityToken(
        issuer: _options.Issuer,
        audience: _options.Audience,
        claims: claims,
        notBefore: now,
        expires: now.Add(_options.Expiration),
        signingCredentials: _options.SigningCredentials);
    var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

    var response = new
    {
        access_token = encodedJwt,
        expires_in = (int)_options.Expiration.TotalSeconds
    };

    // Serialize and return the response
    context.Response.ContentType = "application/json";
    await context.Response.WriteAsync(JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }));
}

创建你的中间件:

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;

namespace SimpleTokenProvider
{
    public class TokenProviderMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly TokenProviderOptions _options;

        public TokenProviderMiddleware(
            RequestDelegate next,
            IOptions<TokenProviderOptions> options)
        {
            _next = next;
            _options = options.Value;
        }

        public Task Invoke(HttpContext context)
        {
            // If the request path doesn't match, skip
            if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
            {
                return _next(context);
            }

            // Request must be POST with Content-Type: application/x-www-form-urlencoded
            if (!context.Request.Method.Equals("POST")
               || !context.Request.HasFormContentType)
            {
                context.Response.StatusCode = 400;
                return context.Response.WriteAsync("Bad request.");
            }

            return GenerateToken(context);
        }
    }
}

最后连接您的中间件以在您的 startup.cs 中生成令牌

app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));

【讨论】:

  • 感谢所有代码!但是,这会根据我从代码中得知的内容生成访问令牌。我是这种安全类型的新手,所以请告诉我。我当前的解决方案已经创建并传递了访问令牌,这很棒。但是,如果您访问 Google 的开发者控制台,您可以创建 API 密钥,允许您使用密钥(而不是访问令牌)访问他们的 API。示例密钥:'AIzaSyBeOqHuSDFjHrpZWCyRWgT3nzIZHkWCjrRg'。这比访问令牌短得多。每个用户的每个可能使用它的应用程序都可能有几个 API 密钥问题。我不知道如何创建这些。
  • 对不起。我建议然后为此创建您自己的提供程序。在映射到用户身份的表中创建并存储 base.64 代码。然后当你的 API 被访问时,它将检查代码是否处于活动状态并且存在并且具有适当的权限。
  • 如果您只想创建密钥,请使用 sha256()
  • @206mph 您只是想生成密钥吗?
  • @Marcus,您能否详细说明并提供一些代码示例? API 如何检查代码是否处于活动状态?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-03
相关资源
最近更新 更多