身份认证服务器

介绍

Identity Server  是一个开源的OpenID Connect和OAuth 2.0框架。它可用于使您的应用程序成为服务器上的身份验证/单点登录。它还可以为第三方客户端发出访问令牌。本文档介绍如何将IdentityServer4(版本2.0+)集成到项目中。

启动项目

本文档假定您已经从启动模板创建了基于ASP.NET Core的项目(包括Module Zero)   并将其设置为可用。我们 为此演示创建了一个  ASP.NET Core MVC启动项目

安装

有两个NuGet包:

由于EF Core软件包已经依赖于第一个软件包,因此您只需将Abp.ZeroCore.IdentityServer4.EntityFrameworkCore  软件包安装到您的项目中即可。将其安装到包含DbContext的项目(默认模板的.EntityFrameworkCore项目):

Install-Package Abp.ZeroCore.IdentityServer4.EntityFrameworkCore

然后,您可以向模块添加依赖项   (通常,添加到您的EntityFrameworkCore项目):

[DependsOn(typeof(AbpZeroCoreIdentityServerEntityFrameworkCoreModule))]
public class MyModule : AbpModule
{
    //...
}

组态

配置和使用带有Abp.ZeroCore的IdentityServer4类似于独立使用IdentityServer4。您应该阅读  自己的文档 以更好地了解它的工作原理。在本文档中,我们仅显示将其集成到Abp.ZeroCore中所需的其他配置。

启动类

在ASP.NET Core Startup类中,我们必须将IdentityServer添加到服务集合和ASP.NET Core中间件管道中。突出显示,以下是与标准IdentityServer4用法的区别:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        //...
        
            services.AddIdentityServer()
                .AddDeveloperSigningCredential()
                .AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources())
                .AddInMemoryApiResources(IdentityServerConfig.GetApiResources())
                .AddInMemoryClients(IdentityServerConfig.GetClients())
                .AddAbpPersistedGrants<IAbpPersistedGrantDbContext>()
                .AddAbpIdentityServer<User>(); ;

        //...
    }

    public void Configure(IApplicationBuilder app)
    {
        //...

            app.UseJwtTokenMiddleware("IdentityBearer");
            app.UseIdentityServer();
            
        //...
    }
}

我们在启动项目中的app.UseAuthentication()之后添加了services.AddIdentityServer(),刚好在IdentityRegistrar.Register(services)和addedapp.UseJwtTokenMiddleware(“IdentityBearer”)之后。

IdentityServerConfig类

我们使用IdentityServerConfig类来获取身份资源,api资源和客户端。您可以在自己的文档中找到有关此类的更多信息  对于最简单的情况,它可以是如下的静态类:

public static class IdentityServerConfig
{
    public static IEnumerable<ApiResource> GetApiResources()
    {
        return new List<ApiResource>
        {
            new ApiResource("default-api", "Default (all) API")
        };
    }

    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
            new IdentityResources.Email(),
            new IdentityResources.Phone()
        };
    }

    public static IEnumerable<Client> GetClients()
    {
        return new List<Client>
        {
            new Client
            {
                ClientId = "client",
                AllowedGrantTypes = GrantTypes.ClientCredentials.Union(GrantTypes.ResourceOwnerPassword).ToList(),
                AllowedScopes = {"default-api"},
                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                }
            }
        };
    }
}

DbContext更改

AddAbpPersistedGrants()方法用于保存对持久性数据存储的同意响应。为了使用它,YourDbContext必须实现IAbpPersistedGrantDbContext接口,如下所示:

public class YourDbContext : AbpZeroDbContext<Tenant, Role, User, YourDbContext>, IAbpPersistedGrantDbContext
{
    public DbSet<PersistedGrantEntity> PersistedGrants { get; set; }

    public YourDbContext(DbContextOptions<YourDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.ConfigurePersistedGrantEntity();
    }
}

IAbpPersistedGrantDbContext接口定义PersistedGrants DbSet。我们还必须调用如上所示的modelBuilder.ConfigurePersistedGrantEntity()扩展方法,以便为PersistedGrantEntity配置EntityFramework。

请注意,YourDbContext中的此更改会导致新的数据库迁移。因此,请记住使用“Add-Migration”和“Update-Database”命令来更新数据库。

即使您没有调用AddAbpPersistedGrants <YourDbContext>()扩展方法,IdentityServer4也将继续工作,但在这种情况下,用户同意响应将存储在内存数据存储中(重新启动应用程序时会将其清除!) 。

JWT认证中间件

如果我们想要针对同一个应用程序授权客户端,我们可以使用  IdentityServer身份验证中间件  。

首先,将NuServer中的IdentityServer4.AccessTokenValidation包安装到您的项目中:

Install-Package IdentityServer4.AccessTokenValidation

然后我们可以将中间件添加到Startup类,如下所示:

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

我们刚刚在启动项目中的services.AddIdentityServer()行之后添加了这个。

IdentityServer4.AccessTokenValidation状态

该  IdentityServer4.AccessTokenValidation  包是没有准备好ASP.NET 2.0的核心还没有(在写这篇文章的时间)。有关详细信息,请参阅https://github.com/IdentityServer/IdentityServer4/issues/1055。

测试

我们的身份服务器现在可以从客户端获取请求。我们可以创建一个控制台应用程序来发出请求并获得响应。

虽然IdentityModel NuGet包足以创建客户端并使用您的API,但我们需要以更安全的方式使用API:我们将传入的数据转换为应用程序服务返回的DTO。

更改Program.cs如下所示:

using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.Json;
using IdentityModel.Client;
using Abp.MultiTenancy;
using Abp.Web.Models;
using IdentityServerIntegrationDemo.Users.Dto;
using Newtonsoft.Json;

namespace IdentityServerIntegrationDemo.ConsoleApiClient
{
    class Program
    {
        static void Main(string[] args)
        {
            RunDemoAsync().Wait();
            Console.ReadLine();
        }

        public static async Task RunDemoAsync()
        {
            var accessToken = await GetAccessTokenViaOwnerPasswordAsync();
            await GetUsersListAsync(accessToken);
        }

        private static async Task<string> GetAccessTokenViaOwnerPasswordAsync()
        {
            var disco = await DiscoveryClient.GetAsync("http://localhost:62114");

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

            if (tokenResponse.IsError)
            {
                Console.WriteLine("Error: ");
                Console.WriteLine(tokenResponse.Error);
            }

            Console.WriteLine(tokenResponse.Json);

            return tokenResponse.AccessToken;
        }

        private static async Task GetUsersListAsync(string accessToken)
        {
            var client = new HttpClient();
            client.SetBearerToken(accessToken);

            var response = await client.GetAsync("http://localhost:62114/api/services/app/user/GetAll");
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine(response.StatusCode);
                return;
            }

            var content = await response.Content.ReadAsStringAsync();
            var ajaxResponse = JsonConvert.DeserializeObject<AjaxResponse<PagedResultDto<UserListDto>>>(content);
            if (!ajaxResponse.Success)
            {
                throw new Exception(ajaxResponse.Error?.Message ?? "Remote service throws exception!");
            }

            Console.WriteLine();
            Console.WriteLine("Total user count: " + ajaxResponse.Result.TotalCount);
            Console.WriteLine();
            foreach (var user in ajaxResponse.Result.Items)
            {
                Console.WriteLine($"### UserId: {user.Id}, UserName: {user.UserName}");
                Console.WriteLine(user.ToJsonString(indented: true));
            }
        }
    }
    
    internal class UserListDto
    {
        public int Id { get; set; }
        public string UserName { get; set; }
    }
}

在运行此应用程序之前,请确保您的Web项目已设置并正在运行,因为此控制台应用程序将向Web应用程序发出请求。此外,请确保请求端口(62114)与Web应用程序相同。

nidie.com.cn - 用心与你沟通