IdentityServer4:密码模式

发布时间 2023-11-21 17:21:19作者: 一只小青蛙-呱-呱-dyj

IdentityServer4:密码模式

 

 

 

IdentityServer4:密码授权模式

密码授权模式需要用户的参与,用户在客户端登录界面输入账号和密码,然后客户端将用户的账号和密码(有时也包括客户端的ClientID)发送往认证服务器换取 AccessToken,然后客户端在每次发往资源服务器的 Http请求中添加 AccessToken ,以此来访问资源服务器上受保护的资源。

Api 资源项目

创建项目

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

依赖包

添加依赖包

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

添加认证方案

修改 Program.cs 为如下代码:


using Microsoft.AspNetCore.Authentication.JwtBearer;
using System;

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

            var builder = WebApplication.CreateBuilder(args);

            builder.Services.AddControllers();

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

            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            var app = builder.Build();

            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

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

            app.UseHttpsRedirection();

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

            app.MapControllers();

            app.Run();
        }
    }
}

其中:
(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" };
                });

代码解析:

添加 Api

新增文件:Controllers/ProductController.cs

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

namespace Dotnet.WebApi.Ids4.ProductApi.Controllers
{
    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        /// <summary>
        /// 添加产品
        /// </summary>
        /// <param name="product"></param>
        /// <returns></returns>
        [HttpPost("Add")]
        public ActionResult<string> Add(Product product)
        {
            var productId = product.Id;
            var productName = product.Name;
            //保存操作
            //........
            return $"productId:{productId}, productName:{productName}";
        }
    }
}

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

Product.cs

namespace Dotnet.WebApi.Ids4.PwdClient
{
    internal class Product
    {
        //唯一ID
        public int Id { get; set; }
        //产品名称
        public string? Name { get; set; }
    }
}

认证服务器

创建项目

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

依赖包

添加依赖包

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

配置 IdentityServer4

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

using IdentityServer4.Models;
using IdentityServer4.Test;

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("Product.Read","读取产品信息。"),
                new ApiScope("Product.Add","添加产品信息。"),

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

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

        /// <summary>
        /// 配置客户端应用。
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    //客户端ID。
                    ClientId = "AppProductAddClient",
                    //密码模式
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, 
                    //认证密钥。
                    ClientSecrets =
                    {
                        new Secret("App00000002".Sha256())
                    },
                    //客户端有权访问的范围。
                    AllowedScopes={ "Product.Add" }
                }
            };
        }

        /// <summary>
        /// 配置用户。
        /// </summary>
        /// <returns></returns>
        public static List<TestUser> GetUsers()
        {
            return new List<TestUser>()
            {
                new TestUser
                {
                    SubjectId="1",
                    Username="zhagnsan",
                    Password="123456"
                }
            };
        }
    }
}

密码授权模式需要用户输入用户名和密码来换取 AccessToken, 故相较于客户端模式,需做如下调整:
(1)Client的授权模式修改为密码模式;
IdentityConfig.cs

                new Client
                {
                    ......
                    //客户端ID。
                    ClientId = "AppProductAddClient",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, 
  
                }

(2)添加用户

代码清单;IdentityConfig.cs

        /// <summary>
        /// 配置用户。
        /// </summary>
        /// <returns></returns>
        public static List<TestUser> GetUsers()
        {
            return new List<TestUser>()
            {
                new TestUser
                {
                    SubjectId="1",
                    Username="zhagnsan",
                    Password="123456"
                }
            };
        }

然后在 Program.cs 中注册 IdentityServer 时, 添加用户

            //注册IdentityServer4组件
            builder.Services.AddIdentityServer()
                ......
                .AddTestUsers(IdentityConfig.GetUsers())
             

集成 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())
                .AddTestUsers(IdentityConfig.GetUsers())
                .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.PwdClient

依赖包

添加依赖包

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

Program.cs

将 Program.cs 的代码修改为;

using static System.Runtime.InteropServices.JavaScript.JSType;

namespace Dotnet.WebApi.Ids4.PwdClient
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.Title = "密码授权模式客户端";

            Console.Write("请输入用户名:");
            var userName = Console.ReadLine();
            Console.Write("请输入密码:");
            var userPwd = Console.ReadLine();

            //获取AccessToken
            var token = DataService.GetAccessToken(userName!, userPwd!).Result.AccessToken;
            Console.WriteLine(token);
            //操作API资源
            var result = DataService.AddAPIData(token);
            Console.WriteLine(result);

            Console.ReadKey();
        }
    }
}

DataService.cs

DataService 类 演示了如何从认证服务器获取 AccessToken,然后向资源服务器发送请求获取数据:

using IdentityModel.Client;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Dotnet.WebApi.Ids4.PwdClient
{
    internal class DataService
    {
        private static readonly string ids4Url = "https://localhost:6001/";
        /// <summary>
        /// 获取AccessToken
        /// </summary>
        /// <returns></returns>
        public static async Task<TokenResponse> GetAccessToken(string userName, string userPwd)
        {
            var client = new HttpClient();
            //连接IDS4服务器
            var disco = client.GetDiscoveryDocumentAsync(ids4Url).Result;
            if (disco.IsError)
            {
                Console.WriteLine(disco.Error);
            }
            //客户端信息
            var tokenclient = new TokenClient(client, new TokenClientOptions
            {
                ClientId = "AppProductAddClient",
                ClientSecret = "App00000002",
                Address = disco.TokenEndpoint,
            });
            //根据用户名和密码换取Token
            var token = await tokenclient.RequestPasswordTokenAsync(userName, userPwd, "Product.Add");
            return token;
        }

        private static readonly string apiUrl = "https://localhost:6012/api/product/add";
        /// <summary>
        /// 添加API数据。
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        public static string AddAPIData(string accessToken)
        {
            string result = "";
            // 实例化HttpClient
            HttpClient httpClient = new HttpClient();
            //将AccessToken附加到HTTP请求的头部。
            httpClient.SetBearerToken(accessToken);
            //实例化Product实体模型类
            Product product = new Product()
            {
                Id = 1,
                Name = "EX-升降椅"
            };
            //将Product对象序列化为JSON字符串。
            var s = JsonConvert.SerializeObject(product);
            //实例化HttpContent对象,封装请求内容。
            //设置编码为utf-8,媒体类型是application/json。
            HttpContent httpContent = new StringContent(s, Encoding.UTF8, "application/json");
            //调用PostAsync()方法执行Post请求。
            var data = httpClient.PostAsync(apiUrl, httpContent);
            //如果请求成功
            if (data.Result.StatusCode == HttpStatusCode.OK)
            {
                result = data.Result.Content.ReadAsStringAsync().Result;
            }
            return result;
        }
    }
}

运行成功后,需要用户在控制台终端中输入用户名和密码来换取 AccessToken,然后才能获取到受保护的Api数据。

转载至:https://www.cnblogs.com/easy5weikai/p/17198709.html