ASP.NET Boilerplate + IdentityServer

3

我试图按照https://aspnetboilerplate.com/Pages/Documents/Zero/Identity-Server中的说明实现IdentityServer,但示例无法正常工作。

我从ASP.NET Boilerplate中开始了一个Core 2.0 Angular项目。是否有基于文档的更新工作示例?

存在多个问题之一是与AuthConfigurer.cs有关。

API调用者(客户端)无法通过令牌验证。

事实上,TokenAuthController.cs中有令牌生成代码:

private string CreateAccessToken(IEnumerable<Claim> claims, TimeSpan? expiration = null)
{
    var now = DateTime.UtcNow;
    var jwtSecurityToken = new JwtSecurityToken(
        issuer: _configuration.Issuer,
        audience: _configuration.Audience,
        claims: claims,
        notBefore: now,
        expires: now.Add(expiration ?? _configuration.Expiration),
        signingCredentials: _configuration.SigningCredentials
    );
    return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
}

但是在 Startup 类中,AddIdentityAddAuthentication 创建了不同的令牌值和验证规则。

services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources())
        .AddInMemoryApiResources(IdentityServerConfig.GetApiResources())
        .AddInMemoryClients(IdentityServerConfig.GetClients())
        .AddAbpPersistedGrants<IAbpPersistedGrantDbContext>()
        .AddAbpIdentityServer<User>(); ;

services.AddAuthentication().AddIdentityServerAuthentication("IdentityBearer", options =>
{
    options.Authority = "http://localhost:62114/";
    options.RequireHttpsMetadata = false;
});

令牌可以由双方生成。CreateAccessToken被Angular客户端和API客户端调用,如下所示:

var disco = await DiscoveryClient.GetAsync("http://localhost:21021");

var httpHandler = new HttpClientHandler();
httpHandler.CookieContainer.Add(new Uri("http://localhost:21021/"), new Cookie(MultiTenancyConsts.TenantIdResolveKey, "1")); //Set TenantId
var tokenClient = new TokenClient(disco.TokenEndpoint, "AngularSPA", "secret", httpHandler);
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("admin", "123qwe", "default-api"); //RequestClientCredentialsAsync("default-api");

但是其中一个(根据认证部分)不能通过身份验证。

我需要 API 客户端身份验证和 Angular 客户端身份验证都能够正常工作。

我从以下链接中获取了一些关于双重身份验证的线索:
https://wildermuth.com/2017/08/19/Two-AuthorizationSchemes-in-ASP-NET-Core-2

但我无法解决这个问题。任何评论都非常有价值,以解决这个问题。

1个回答

5
在最后,我设法解决了问题,以下是所需的修改;
1- 在TokenAuthController中有一个令牌创建代码,如下所示;
private static List<Claim> CreateJwtClaims(ClaimsIdentity identity)
        {
            var claims = identity.Claims.ToList();
            var nameIdClaim = claims.First(c => c.Type == ClaimTypes.NameIdentifier);

            // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
            claims.AddRange(new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, nameIdClaim.Value),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
            });

            return claims;
        }

如果您开始使用Identityserver,来自登录的Claims与当前实现完全不同,并且"sub"声明已经添加到了Claims中。因此,不需要单独添加。请根据以下步骤更新:
 private static List<Claim> CreateJwtClaims(ClaimsIdentity identity)
        {
            var claims = identity.Claims.ToList();

            // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
            claims.AddRange(new[]
            {
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
            });

            return claims;
        }

2- 将身份验证添加到启动类中,如下所示;最重要的部分是身份验证模式名称“IdentityBearer”,不要忘记添加它

services.AddAuthentication().AddIdentityServerAuthentication("IdentityBearer", options =>
            {
                options.Authority = "http://localhost:21021/";
                options.RequireHttpsMetadata = false;
            });

3- 但这还不够。因为如果您查看启动身份验证中的配置方法,身份验证将被注册为

app.UseJwtTokenMiddleware(); 

如果您检查它,它使用的是"bearer"模式,而不是我们上面添加的IdentityBearer。因此,我们还需要另一个身份验证注册。也要添加这一行(两者都有)。

    app.UseJwtTokenMiddleware("IdentityBearer");

4- 但是,正如您所看到的,没有一个方法接受字符串参数来添加UseJwtTokenMiddleware,因此需要更新该类。请按照下面所示更改您的类:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;

namespace MyProject.Authentication.JwtBearer
{
    public static class JwtTokenMiddleware
    {
        public static IApplicationBuilder UseJwtTokenMiddleware(this IApplicationBuilder app)
        {
            return UseJwtTokenMiddleware(app, JwtBearerDefaults.AuthenticationScheme);
        }

        public static IApplicationBuilder UseJwtTokenMiddleware(this IApplicationBuilder app, string authenticationScheme)
        {
            return app.Use(async (ctx, next) =>
            {
                if (ctx.User.Identity?.IsAuthenticated != true)
                {
                    var result = await ctx.AuthenticateAsync(authenticationScheme);
                    if (result.Succeeded && result.Principal != null)
                    {
                        ctx.User = result.Principal;
                    }
                }

                await next();
            });
        }        
    }
}

现在你有两种不同的令牌类型和两个不同的验证器。你可以使用基本令牌信息来创建API客户端,并且JWT令牌是由Angular客户端登录创建的。如果你调试每个请求,会尝试传递其中的两个令牌,但只有一个成功就足够了。
如果aspnetboilerplate团队根据这个要求更新示例,那就太好了。

你所说的“sample”,是指文档还是模板? - aaron
实际上,很难看出需要添加app.UseJwtTokenMiddleware("IdentityBearer")来进行二次验证。而且该方法不接受字符串参数作为模式名称。这部分可以在模板中更改。在文档末尾,其他部分可以作为说明添加,因为正如我之前所说,给定示例中两个令牌验证不能同时工作。Api客户端或Angular客户端令牌验证可以正常工作(只有其中一个)。 - Tuğrul Karakaya
在登录过程中添加“sub”声明并不是必需的,如果您开始使用IdentityServer,则会引发错误。 - Tuğrul Karakaya
你可以创建一个问题,或者更好的方式是在仓库上创建一个PR。 - aaron
好的,谢谢。提醒读者一下,我按照 http://docs.identityserver.io/en/release/topics/apis.html 上的步骤来实现的。 - Tuğrul Karakaya
我遇到了同样的问题并解决了它,非常好的答案! - Nitin Sawant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接