• IdentityServer4-用EF配置Client(一)


    一、背景

    IdentityServer4的介绍将不再叙述,百度下可以找到,且官网的快速入门例子也有翻译的版本。这里主要从Client应用场景方面介绍对IdentityServer4的应用。

    首先简要介绍ID Token和Access Token:

    Access Token是授权第三方客户端访问受保护资源的令牌。 ID Token是第三方客户端标识用户身份认证的问令牌,是JSON Web Token格式。


    二、Client应用场景介绍

    Client类是为OpenID Connect或OAuth 2.0 协议建模的。

    我们先看官网快速入门中给的Client例子

     public static IEnumerable<Client> GetClients()
            {
                // client credentials client
                return new List<Client>
                {
                    new Client
                    {
                        ClientId = "Client",
                        AllowedGrantTypes = GrantTypes.ClientCredentials,
                        ClientSecrets =
                        {
                            new Secret("secret".Sha256())
                        },
                        AllowedScopes = { "api1" }
                    },
    
                    // resource owner password grant client
                    new Client
                    {
                        ClientId = "ro.client",
                        AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
    
                        ClientSecrets = 
                        {
                            new Secret("secret".Sha256())
                        },
                        AllowedScopes = { "api1" }                },
    
                    // OpenID Connect hybrid flow and client credentials client (MVC)
                    new Client
                    {
                        ClientId = "mvc",
                        ClientName = "MVC Client",
                        AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
    
                        ClientSecrets =
                        {
                            new Secret("secret".Sha256())
                        },
    
                        RedirectUris = { "http://localhost:5002/signin-oidc" },
                        PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
    
                        AllowedScopes =
                        {
                            IdentityServerConstants.StandardScopes.OpenId,
                            IdentityServerConstants.StandardScopes.Profile,
                            "api1"
                        },
                        AllowOfflineAccess = true
                    },
    
                    // JavaScript Client
                    new Client
                    {
                        ClientId = "js",
                        ClientName = "JavaScript Client",
                        AllowedGrantTypes = GrantTypes.Implicit,
                        AllowAccessTokensViaBrowser = true,
    
                        RedirectUris = { "http://localhost:5003/callback.html" },
                        PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
                        AllowedCorsOrigins = { "http://localhost:5003" },
    
                        AllowedScopes =
                        {
                            IdentityServerConstants.StandardScopes.OpenId,
                            IdentityServerConstants.StandardScopes.Profile,
                            "api1"
                        },
                    }
                };
            }

    里面主要介绍四种Client应用场景。

    (1)客户端模式(AllowedGrantTypes = GrantTypes.ClientCredentials

        这是一种最简单的授权方式,应用于服务于服务之间的通信,token通常代表的是客户端的请求,而不是用户。

        使用这种授权类型,会向token endpoint发送token请求,并获得代表客户机的access token。客户端通常必须使用token endpoint的Client ID和secret进行身份验证。

        适用场景:用于和用户无关,服务与服务之间直接交互访问资源

    (2)密码模式(ClientAllowedGrantTypes = GrantTypes.ResourceOwnerPassword

        该方式发送用户名和密码到token endpoint,向资源服务器请求令牌。这是一种“非交互式”授权方法。

        官网上称,为了解决一些历史遗留的应用场景,所以保留了这种授权方式,但不建议使用。

        适用场景:用于当前的APP是专门为服务端设计的情况。

    (3)混合模式和客户端模式(ClientAllowedGrantTypes =GrantTypes.HybridAndClientCredentials

           ClientCredentials授权方式在第一种应用场景已经介绍了,这里主要介绍Hybrid授权方式。Hybrid是由Implicit和Authorization code结合起来的一种授权方式。其中Implicit用于身份认证,ID token被传输到浏览器并在浏览器进行验证;而Authorization code使用反向通道检索token和刷新token。

        推荐使用Hybrid模式。

        适用场景:用于MVC框架,服务器端 Web 应用程序和原生桌面/移动应用程序。

    (4)简化模式(ClientAllowedGrantTypes =GrantTypes.Implicit

        Implicit要么仅用于服务端和JavaScript应用程序端进行身份认证,要么用于身份身份验证和access token的传输。

        在Implicit中,所有token都通过浏览器传输的。

        适用场景:JavaScript应用程序。


    三、Server端搭建

    为了介绍IdentityServer4的Client应用场景,我们需要先搭建IdentityServer服务端。

    这里搭建的是使用EF Core来做数据操作,保存到SQL Server中。

    (1)新建API项目

    (2)安装IdentityServer4.EntityFramework包

    (3)安装IdentityServer4包

    (4)右键项目的属性,编辑项目的.csproj文件

    添加如下元素

    <ItemGroup>
        <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
    </ItemGroup>

    如图:

    (5)cmd管理员身份进入项目目录路径(D:IdentityServer4Server),运行:dotnet ef

    (6)项目内添加Config.cs类,代码如下

     public class Config
        {
            public static List<TestUser> GetUsers()
            {
                return new List<TestUser>
                {
                    new TestUser
                    {
                        SubjectId = "1",
                        Username = "alice",
                        Password = "password",
    
                      Claims = new List<Claim>(){new Claim(JwtClaimTypes.Role,"superadmin") }
                    },
                    new TestUser
                    {
                        SubjectId = "2",
                        Username = "bob",
                        Password = "password",
    
                        Claims = new List<Claim>
                        {
                            new Claim("name", "Bob"),
                            new Claim("website", "https://bob.com")
                        },
                    }
                };
            }
    
            public static IEnumerable<Client> GetClients()
            {
                // client credentials client
                return new List<Client>
                {
                    new Client
                    {
                        ClientId = "Client",
                        AllowedGrantTypes = GrantTypes.ClientCredentials,
                        ClientSecrets =
                        {
                            new Secret("secret".Sha256())
                        },
                        AllowedScopes = { "api1" }
                    },
    
                    // resource owner password grant client
                    new Client
                    {
                        ClientId = "ro.client",
                        AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
    
                        ClientSecrets =
                        {
                            new Secret("secret".Sha256())
                        },
                        AllowedScopes = { "api1" }
                    },
    
                    // OpenID Connect hybrid flow and client credentials client (MVC)
                    new Client
                    {
                        ClientId = "mvc",
                        ClientName = "MVC Client",
                        AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
    
                        ClientSecrets =
                        {
                            new Secret("secret".Sha256())
                        },
    
                        RedirectUris = { "http://localhost:5002/signin-oidc" },
                        PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
    
                        AllowedScopes =
                        {
                            IdentityServerConstants.StandardScopes.OpenId,
                            IdentityServerConstants.StandardScopes.Profile,
                            "api1"
                        },
                        AllowOfflineAccess = true
                    },
    
                    // JavaScript Client
                    new Client
                    {
                        ClientId = "js",
                        ClientName = "JavaScript Client",
                        AllowedGrantTypes = GrantTypes.Implicit,
                        AllowAccessTokensViaBrowser = true,
    
                        RedirectUris = { "http://localhost:5003/callback.html" },
                        PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
                        AllowedCorsOrigins = { "http://localhost:5003" },
    
                        AllowedScopes =
                        {
                            IdentityServerConstants.StandardScopes.OpenId,
                            IdentityServerConstants.StandardScopes.Profile,
                            "api1"
                        },
                    }
                };
            }
            public static IEnumerable<IdentityResource> GetIdentityResources()
            {
                return new List<IdentityResource>
                {
                    new IdentityResources.OpenId(),
                    new IdentityResources.Profile(),
                };
            }
    
            public static IEnumerable<ApiResource> GetApiResources()
            {
                return new List<ApiResource>
                {
                    new ApiResource("api1", "My API")
                };
            }

    添加引用:

    using IdentityModel;

    using IdentityServer4;

    using IdentityServer4.Models;

    using IdentityServer4.Test;

    using System.Collections.Generic;

    using System.Security.Claims;

    (7)编辑Startup.cs文件的ConfigureServices方法,改成如下代码。

    public void ConfigureServices(IServiceCollection services)
            {
                const string connectionString = @"Server=localhost;database=IdentityServer4;User ID=sa;Password=Pwd;trusted_connection=yes";
                var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
    
                // configure identity server with in-memory stores, keys, clients and scopes
                services.AddIdentityServer()
                    .AddDeveloperSigningCredential()
                    .AddTestUsers(Config.GetUsers())
                    // this adds the config data from DB (clients, resources)
                    .AddConfigurationStore(options =>
                    {
                        options.ConfigureDbContext = builder =>
                            builder.UseSqlServer(connectionString,
                                sql => sql.MigrationsAssembly(migrationsAssembly));
                    })
                    // this adds the operational data from DB (codes, tokens, consents)
                    .AddOperationalStore(options =>
                    {
                        options.ConfigureDbContext = builder =>
                            builder.UseSqlServer(connectionString,
                                sql => sql.MigrationsAssembly(migrationsAssembly));
    
                        // this enables automatic token cleanup. this is optional.
                        options.EnableTokenCleanup = false;//是否从数据库清楚令牌数据,默认为false
                        options.TokenCleanupInterval = 300;//令牌过期时间,默认为3600秒,一个小时
                    });
                //.AddInMemoryClients(Config.GetClients());
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            }

    添加引用:

    using Microsoft.EntityFrameworkCore;

    using System.Reflection;

    (8)cmd管理员身份进入到项目目录路径(D:IdentityServer4ServerServer),注意,多了一层目录,分别运行以下两条指令:

    dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
    
    dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb

    运行完后,项目中会多了一个Data文件夹

    (9)在Startup.cs中添加初始化数据库方法。

    private void InitializeDatabase(IApplicationBuilder app)
    {
        using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
        {
            serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
    
            var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
            context.Database.Migrate();
            if (!context.Clients.Any())
            {
                foreach (var client in Config.GetClients())
                {
                    context.Clients.Add(client.ToEntity());
                }
                context.SaveChanges();
            }
    
            if (!context.IdentityResources.Any())
            {
                foreach (var resource in Config.GetIdentityResources())
                {
                    context.IdentityResources.Add(resource.ToEntity());
                }
                context.SaveChanges();
            }
    
            if (!context.ApiResources.Any())
            {
                foreach (var resource in Config.GetApiResources())
                {
                    context.ApiResources.Add(resource.ToEntity());
                }
                context.SaveChanges();
            }
        }
    }

    添加引用:

    using IdentityServer4.EntityFramework.DbContexts;

    using IdentityServer4.EntityFramework.Mappers;

    (10)在Startup.cs中的Configure方法修改成以下代码。

      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                //if (env.IsDevelopment())
                //{
                //    app.UseDeveloperExceptionPage();
                //}
                InitializeDatabase(app);
                //app.UseMvc();
            }

    到这里,把项目以控制台形式运行

    点击运行,可以跑起来,且生成数据库IdentityServer4DB。

    关于Client的说明可以查阅官网资料:https://identityserver4.readthedocs.io/en/release/reference/client.html


    源码地址:https://github.com/Bingjian-Zhu/Server.git

    服务端准备好之后,下篇文章开始介绍Client客户端的应用。

     

  • 相关阅读:
    JavaScript引用类型
    Java08_方法
    网络爬虫(一)
    openCV(四)——鼠标绘制
    openCV(三)——视频
    openCV(二)——基础绘制函数
    openCV(一) 读取保存图像
    JAVA07-数组
    JAVA06-while循环,do while循环
    JAVA05-switch多重选择
  • 原文地址:https://www.cnblogs.com/FireworksEasyCool/p/10132356.html
Copyright © 2020-2023  润新知