IdentityServer4实战详解(HyBrid混合模式篇)
HyBrid混合模式
概念描述
书接上篇implict隐式模式,HyBrid模式是授权码的简化模式的进阶版,更加的安全,解决了在混合模式中访问令牌直接暴露在浏览器的风险
在前面的快速启动中,我们研究了API访问和用户身份验证。现在我们要把这两个部分结合起来。
OpenID Connect & OAuth 2.0组合的美妙之处在于,您可以通过一个协议和一个与令牌服务的交换来实现。
在前面的quickstart中,我们使用了OpenID连接隐式流。在隐式流中,所有令牌都通过浏览器传输,这对于身份令牌来说是完全没问题的。现在我们还想请求一个访问令牌。
访问令牌比身份令牌更加敏感,如果不需要,我们不希望将它们暴露于“外部”世界。 OpenID Connect包含一个名为“混合流”的流程,它可以让我们两全其美,身份令牌通过浏览器通道传输,因此客户可以在做更多工作之前验证它。 如果验证成功,客户端会打开令牌服务的后端通道来检索访问令
牌。
没有多少必要的修改。首先,我们希望允许客户端使用混合流,此外,我们还希望客户端允许执行服务器到服务器的API调用,这些调用不在用户的上下文中(这与我们的客户端凭证quickstart非常相似)。使用AllowedGrantTypes属性表示。
接下来,我们需要添加一个客户端secret。这将用于检索后通道上的访问令牌。
最后,我们还为客户端提供了对offline_access范围的访问权限——这允许为长期存在的API访问请求刷新令牌:
一、HyBrid混合模式
1.Ids4认证服务器修改
使用上节创建好的项目进行修改,修改的地方较少,只需要改配置
修改Config.cs内容,因为新版ids4与旧版不同,请认真对比修改几个细微的地方
public static class Config
{
//修改
public static IEnumerable<ApiResource> GetApiResources()
{
//因为在ids4的新版中与老版有所不同,ApiScopes必须要添加
return new List<ApiResource>
{
new ApiResource("api1", "My API")
{
Scopes = { "api1" }
}
};
}
//新增加
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("api1")
};
public static IEnumerable<Client> GetClients()
{
return new List<Client>()
{
new Client(){
ClientId="client1",
ClientName="mvc client",
RequirePkce = false,
//修改授权模式
AllowedGrantTypes=GrantTypes.HybridAndClientCredentials,
//添加客户端密钥
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris={"http://localhost:5001/signin-oidc"},
PostLogoutRedirectUris={"http://localhost:5001/signout-callback-oidc"},
FrontChannelLogoutUri = "http://localhost:5000/signout-idsrv",
AllowAccessTokensViaBrowser=true,
//添加Scope
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
//添加范围
"api1"
},
//客户端提供了对offline_access范围的访问权限,这允许为长期存在的API访问请求刷新令牌:
AllowOfflineAccess = true
}
};
}
public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "password"
},
new TestUser
{
SubjectId = "2",
Username = "bob",
Password = "password"
}
};
}
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
}
Program.cs修改,添加APIScope资源,否则会报错
.AddInMemoryApiScopes(Config.ApiScopes)
2.修改客户端配置
MVC客户端的修改也是最小的——ASP.NET核心的OpenID连接处理器对混合流有内置的支持,所以我们只需要更改一些配置值。
我们配置 ClientSecret 以匹配IdentityServer上的secret。添加 offline_access 和 api1 作用域,并将 ResponseType 设置为 code id_token (这基本上意味着“使用混合流”)
修改Program新增以下配置
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("api1");
options.Scope.Add("offline_access");
以下是完整配置,与上节对比
using System.IdentityModel.Tokens.Jwt;
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext =>
SetSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext =>
SetSameSite(cookieContext.Context, cookieContext.CookieOptions);
});
//部分浏览器版本较新SameSite不设置会出现报错,
void SetSameSite(HttpContext httpContext, CookieOptions options)
{
if (options.SameSite == SameSiteMode.None)
{
if (httpContext.Request.Scheme != "https")
{
options.SameSite = SameSiteMode.Unspecified;
}
}
}
builder.Services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
builder.Services.AddMvc();
//AddAuthentication将认证服务添加到DI
builder.Services.AddAuthentication(options => {
options.DefaultScheme = "Cookies";//设置Cookies为主要认证手段
options.DefaultChallengeScheme = "oidc";//当需要登录时使用OpenID Connect方案
})
.AddCookie("Cookies")//使用AddCookie添加可以处理cookie的处理程序。
.AddOpenIdConnect("oidc", options => {
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
//新增的响应
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
// 是否从UserInfoEndpoint获取Claims
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("api1");
options.Scope.Add("offline_access");
options.RequireHttpsMetadata = false;
options.ClientId = "client1";
options.SaveTokens = true;
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
//添加Cookie,不让报错,无法正常使用
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
// 看登录的用户是否有权限
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
AddOpenIdConnect用于配置执行OpenID连接协议的处理程序。授权表明我们信任身份服务器。然后我们通过ClientId识别这个客户端。一旦OpenID连接协议完成,SignInScheme将使用cookie处理程序发出cookie。
savetoken用于将标识符从IdentityServer中持久化到cookie中(稍后需要它们)。
此外,我们关闭了JWT声明类型映射,以允许众所周知的声明(例如’sub’和’idp’)通过非混淆的声明:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
3.修改客户端view/home/index
直接在后面复制代码,获得从服务器返回的id_token信息,
<dt>access token</dt>
<dd>@await ViewContext.HttpContext.GetTokenAsync("access_token")</dd>
<dt>refresh token</dt>
<dd>@await ViewContext.HttpContext.GetTokenAsync("refresh_token")</dd>
效果展示
二、URL理解
1. 因为没有授权,客户端重定向到授权服务器
2.重定向,发起GET请求,携带必要的参数
3.因为未授权,定向到了登录界面,验证身份
4.登录成功后重定向post,给客户端发送code和id_token等数据
发送code等数据
通过负载可以看到携带了code id_token返回
5、通过code和idtoken获得访问令牌和身份令牌
6.拿到access_token,通过cookie策略写入,然后携带cookie去请求,就有权限了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了