IdentityServer4实战详解(HyBrid混合模式篇)

概念描述

参考雨夜朦胧大佬文章
参考腾飞(Jesse)大佬文章

书接上篇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去请求,就有权限了

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有诗亦有远方

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值