IdentityServer4:客户端模式

IdentityServer4:客户端模式

客户端模式不需要用户的参与,客户端(Client)向一个认证服务器发送 ClientID 和密码来换取AccessToken,然后使用AccessToken访问资源服务器受保护的资源(API)。

Api 资源项目

创建项目

打开 VS,创建一个“AspNet Core WebApi” 项目, 名为:Dotnet.WebApi.Ids4.CustomerApi

依赖包

添加依赖包

    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.3" />

添加认证方案

修改 Program.cs 为如下代码:


using Microsoft.AspNetCore.Authentication.JwtBearer;

namespace Dotnet.WebApi.Ids4.CustomerApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.Title = "CustomerAPI服务器";

            var builder = WebApplication.CreateBuilder(args);

            builder.Services.AddControllers();

            // Add services to the container.
            builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    //IdentityServer4地址
                    options.Authority = "https://localhost:6001";
                    //认证的ApiResource名称
                    options.Audience = "CustomerAPIResource";
                    //使用JWT认证类型
                    options.TokenValidationParameters.ValidTypes = new[] { "at+jwt" };
                });

            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.Urls.Add("https://*:6011");
            app.UseHttpsRedirection();

            //身份验证
            app.UseAuthentication();
            //授权
            app.UseAuthorization();

            app.MapControllers();

            app.Run();
        }
    }
}

代码解析:
(1) "at+jwt": 表示 JWT的类型(JsonWebTokenTypes)为 AccessToken。

(1)添加 JWT 认证:

            builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    //IdentityServer4地址
                    options.Authority = "https://localhost:6001";
                    //认证的ApiResource名称
                    options.Audience = "CustomerAPIResource";
                    //使用JWT认证类型
                    options.TokenValidationParameters.ValidTypes = new[] { "at+jwt" };
                });
 https://localhost:6001 是认证服务器地址。

添加 Api

新增文件:Controllers/CustomerController.cs

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Dotnet.WebApi.Ids4.CustomerApi.Controllers
{
    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class CustomerController : ControllerBase
    {
        /// <summary>
        /// 获取客户信息列表。
        /// </summary>
        /// <returns></returns>
        [HttpGet("GetList")]
        public IEnumerable<Customer> GetList()
        {
            return new List<Customer>
            {
                new Customer{ Id=1, Name="客户1", Phone="电话1"},
                new Customer{ Id=2, Name="客户2", Phone="电话2"},
                new Customer{ Id=3, Name="客户3", Phone="电话3"},
            };
        }
    }
}

其中:
(1)在控制器上添加特性:[Authorize],这样只有登录用户才能访问,这样就起到保护了Api资源的目的。

Customer.cs

namespace Dotnet.WebApi.Ids4.CustomerApi
{
    /// <summary>
    /// 客户实体模型
    /// </summary>
    public class Customer
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Phone { get; set; }
    }
}

认证服务器

创建项目

打开 VS,创建一个“AspNet Core 空” 项目,名为:Dotnet.WebApi.Ids4.AuthService

依赖包

添加依赖包

<PackageReference Include="IdentityServer4" Version="4.1.2" />

配置 IdentityServer4

创建文件:IdentityConfig.cs,添加如下代码:

using IdentityServer4.Models;

namespace Dotnet.WebApi.Ids4.AuthService
{
    public static class IdentityConfig
    {
        /// <summary>
        /// 配置API作用域。
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<ApiScope> GetApiScopes()
        {
            return new List<ApiScope>
            {
                //客户相关API作用域
                new ApiScope("Customer.Read","读取客户信息。"),
                new ApiScope("Customer.Add","添加客户信息。"),

                //共享API作用域
                new ApiScope("News","新闻信息。")
            };
        }

        /// <summary>
        /// 配置ApiResource。
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<ApiResource> GetApiResources()
        {
            //将多个具体的APIScope归为一个ApiResource。
            return new List<ApiResource>()
            {
                new ApiResource("CustomerAPIResource", "客户资源")
                {
                    Scopes={ "Customer.Read", "Customer.Add", "News" }
                }
            };
        }

        /// <summary>
        /// 配置客户端应用。
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    //客户端ID。
                    ClientId = "AppCustomerReadClient",
                    //客户端凭据模式
                    AllowedGrantTypes = GrantTypes.ClientCredentials, 
                    //认证密钥。
                    ClientSecrets =
                    {
                        new Secret("App00000001".Sha256())
                    },
                    //客户端有权访问的范围。
                    AllowedScopes={ "Customer.Read" }
                }
            };
        }
    }
}

其中,如下代码添加了 Client,并将其授权模式设置为:客户端模式, 并设置密码,和 Scope:

                new Client
                {
                    //客户端ID。
                    ClientId = "AppCustomerReadClient",
                    //客户端凭据模式
                    AllowedGrantTypes = GrantTypes.ClientCredentials, 
                    //认证密钥。
                    ClientSecrets =
                    {
                        new Secret("App00000001".Sha256())
                    },
                    //客户端有权访问的范围。
                    AllowedScopes={ "Customer.Read" }
                }

集成 IdentityServer4

修改 Program.cs 为如下代码:

namespace Dotnet.WebApi.Ids4.AuthService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.Title = "认证和授权服务器";

            var builder = WebApplication.CreateBuilder(args);

            //注册IdentityServer4组件
            builder.Services.AddIdentityServer()
                .AddInMemoryApiScopes(IdentityConfig.GetApiScopes())
                .AddInMemoryApiResources(IdentityConfig.GetApiResources())
                .AddInMemoryClients(IdentityConfig.GetClients())
                .AddDeveloperSigningCredential(); // 添加临时内存中的证书

            var app = builder.Build();
            //修改端口号
            app.Urls.Add("https://*:6001");

            //添加IDS4中间件。
            //在浏览器中输入如下地址访问 IdentityServer4 的发现文档:https://localhost:6001/.well-known/openid-configuration
            app.UseIdentityServer();

            app.Run();
        }
    }
}

其中,app.Urls.Add("https://*:6001"); 设置认证服务器的监听端口为:6001

客户端模式客户端

创建项目

新控制台项目,名为:Dotnet.WebApi.Ids4.Client

依赖包

添加依赖包

<PackageReference Include="IdentityServer4" Version="4.1.2" />

Program.cs

将 Program.cs 的代码修改为;

namespace Dotnet.WebApi.Ids4.Client
{
    internal class Program
    {
        static void Main()
        {
            Console.Title = "客户端模式-客户端";

            //获取AccessToken
            var token = DataService.GetAccessToken();
            Console.WriteLine(token);

            //获取API数据
            var data = DataService.GetAPIData(token);
            Console.WriteLine(data.Result);

            Console.ReadKey();
        }
    }
}

DataService.cs

using IdentityModel.Client;
using Newtonsoft.Json.Linq;

namespace Dotnet.WebApi.Ids4.Client
{
    internal class DataService
    {
        private static readonly string ids4Url = "https://localhost:6001/";
        /// <summary>
        /// 获取AccessToken。
        /// </summary>
        /// <returns></returns>
        public static string GetAccessToken()
        {
            var client = new HttpClient();
            //获取IDS4发现文档
            var disco = client.GetDiscoveryDocumentAsync(ids4Url).Result;
            if (disco.IsError)
            {
                Console.WriteLine(disco.Error);
                return "IDS4服务器无法访问。";
            }
            //请求token
            var tokenResponse = client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
            {
                //配置客户端信息
                Address = disco.TokenEndpoint,//获取token的地址
                ClientId = "AppCustomerReadClient",//客户端ID
                ClientSecret = "App00000001",//客户端密钥
                Scope = "Customer.Read"//访问的资源范围
            }).Result;

            if (tokenResponse.IsError)
            {
                Console.WriteLine(tokenResponse.Error);
                return "获取AccessToke错误。";
            }
            //以JSON格式返回Token
            return tokenResponse.Json.TryGetValue("access_token").ToString();
        }

        private static readonly string dataUrl = "https://localhost:6011/api/customer/getlist";
        /// <summary>
        /// 获取API数据。
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        public static async Task<JArray?> GetAPIData(string accessToken)
        {
            JArray? jArray = null;
            if (string.IsNullOrEmpty(accessToken)) return null;
            //调用API
            var client = new HttpClient();
            //将AccessToken附加到HTTP请求的头部。
            client.SetBearerToken(accessToken);
            //调用远程的API。
            var response = await client.GetAsync(dataUrl);
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine(response.StatusCode);
            }
            else
            {
                var content = await response.Content.ReadAsStringAsync();
                jArray = JArray.Parse(content);
            }
            return jArray;
        }
    }
}

posted @ 2023-03-09 15:20  easy5  阅读(177)  评论(0编辑  收藏  举报