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;
}
}
}