Identity Server 4

準(zhǔn)備

  1. 建立Identity Provider項(xiàng)目
  • IdentityServer4.Templates
  • https://github.com/IdentityServer/IdentityServer4.Templates
  • 安裝工具:
    • dotnet new -i identityserver4.templates
    • 重置 “dotnet new” 功能列表: dotnet new --debug:reinit
  • 模板:
    • dotnet new is4empty
    • dotnet new is4ui
    • dotnet new is4inmem
    • dotnet new is4aspid
    • dotnet new is4ef
    • dotnet new is4admin (收費(fèi))

創(chuàng)建項(xiàng)目

  • dotnet new -i identityserver4.templates
  • dotnet new is4aspid --name BlogIdp
  • 升級(jí)為.NET Core 2.1, 更新Nuget包
  • 配置Hsts|HttpsRedirection
   //注冊(cè)Hsts
            services.AddHsts(options =>
            {
                options.Preload = true;
                options.IncludeSubDomains = true;
                options.MaxAge = TimeSpan.FromDays(60);
            });

            //配置HTTP重定向
            services.AddHttpsRedirection(options =>
            {
                options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
                options.HttpsPort = 6001;
            });

MVC 測(cè)試

  • 安全/機(jī)密客戶端(Confidential Client), 它是傳統(tǒng)的服務(wù)器端Web應(yīng)用.
  • 它需要長(zhǎng)時(shí)間訪問(wèn)(long-lived access), 所以需要refresh token. 那么它可以使用Authorization Code Flow或Hybrid Flow.
  • Hybrid Flow是相對(duì)高級(jí)一些的, 它可以讓客戶端首先從授權(quán)端點(diǎn)獲得一個(gè)ID Token并通過(guò)瀏覽器(front-channel)傳遞過(guò)來(lái), 這樣我們就可以驗(yàn)證這個(gè)ID Token. 如果驗(yàn)證成功然后, 客戶端再打開(kāi)一個(gè)后端通道(back-channel), 從Token端點(diǎn)獲取Access Token.
  1. 身份認(rèn)證請(qǐng)求


  • 第一行的URI: "/authorize" 就是授權(quán)端點(diǎn)(Authorization Endpoint), 它位于身份提供商(Identity provider, IDP)那里. 這個(gè)URI可以從前面介紹的discovery document里面找到.
  • 第二行 response_type=code id_token, 它決定了采取了哪一種Hybrid流程(參考上面那三個(gè)圖).
  • 第三行 client_id=xxxx, 這是客戶端的身份標(biāo)識(shí).
  • 第四行 redirect_uri=https...., 這是客戶端那里的重定向端點(diǎn)(Redirection Endpoint).
  • 第五行 scope=openid profile email, 這就是客戶端所請(qǐng)求的scopes.
  1. Hybrid Flow


    Hybrid Flow
  • 為什么要返回兩次ID Token呢? 這是因?yàn)榈?4)步里面請(qǐng)求Token的時(shí)候要求客戶端身份認(rèn)證, 這時(shí)請(qǐng)求Token的時(shí)候需要提供Authorization Code, Client ID和 Client Secret, 這些secret并不暴露給外界, 這些東西是由客戶端服務(wù)器通過(guò)后端通道傳遞給Token端點(diǎn)的. 而第一次獲得的ID Token是從前端通道(瀏覽器)返回的. 當(dāng)這個(gè)ID Token被驗(yàn)證通過(guò)之后, 也就證明了當(dāng)前用戶到底是誰(shuí).

code demo

  1. 新建MVC
  2. 修改端口為7000,7001
  3. 修改Idp項(xiàng)目config.cs
 new Client
                {
                    ClientId = "mvcclient",
                    ClientName = "MVC客戶端",

                    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
                    ClientSecrets = { new Secret("49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256()) },

                    RedirectUris = { "http://localhost:7001/signin-oidc" },
                    FrontChannelLogoutUri = "http://localhost:7001/signout-oidc",
                    PostLogoutRedirectUris = { "http://localhost:7001/signout-callback-oidc" },

                    AllowOfflineAccess = true, //offline_access(refresh token)
                    AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId }// { "openid", "profile", "api1" }
                },
  1. 修改mvc項(xiàng)目Startup
  public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            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;
            });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);


            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

            services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
                .AddCookie("Cookies", options =>
                {
                    options.AccessDeniedPath = "/Authorization/AccessDenied";
                })
                .AddOpenIdConnect("oidc", options =>
                {
                    options.SignInScheme = "Cookies";
                    options.Authority = "https://localhost:6001";
                    options.RequireHttpsMetadata = true;
                    options.ClientId = "mvcclient";
                    options.ResponseType = "code id_token";
                    options.Scope.Clear();
                    options.Scope.Add("openid"); //與Idp中config對(duì)應(yīng)
                    //options.Scope.Add("profile");
                    //options.Scope.Add("email");
                    //options.Scope.Add("restapi");

                    options.SaveTokens = true;
                    options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";
                    options.GetClaimsFromUserInfoEndpoint = true;
                });
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseAuthentication();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
  1. HomeController 測(cè)試
 [Authorize]
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
  ......
跳轉(zhuǎn)驗(yàn)證
  1. 添加更多獲取資源
 public async Task<IActionResult> About()
        {
            var idToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken);

            ViewData["idToken"] = idToken;

            return View();
        }
  1. Config.cs添加
  public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new IdentityResource[]
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResources.Email(),
            };
        }
 public static IEnumerable<ApiResource> GetApis()
        {
            return new ApiResource[]
            {
                new ApiResource("restapi", "My RESTful API")
            };
        }
   AllowOfflineAccess = true, //offline_access(開(kāi)啟refresh token)
   AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        IdentityServerConstants.StandardScopes.Email,
                        "restapi"
                    }// { "openid", "profile", "api1" }
  1. 修改html
@{
    ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2>
<h3>IdToken:@ViewData["idToken"]</h3>

<dl>
    @foreach (var claim in User.Claims)
    {
        <dt>@claim.Type</dt>
        <dd>@claim.Value</dd>
    }
</dl>

保護(hù)API資源

  1. 安裝包 `IdentityServer4.AccessTokenValidation
  2. 注冊(cè)
 // 注冊(cè)IdentityServer
            services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = "https://localhost:6001";
                    options.ApiName = "restapi";
                });

  1. config
    app.UseAuthentication();
  2. api中加驗(yàn)證
    • 屬性標(biāo)簽 [Authorize]
    • 全局filter
  //設(shè)置全局filter保護(hù)api需要認(rèn)真用戶才可訪問(wèn)
            services.Configure<MvcOptions>(options =>
            {
                var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            });
  1. NoAuth


    401 error
  2. MVC客戶端
  • 安裝IdentityModel
  • 修改HomeController
  public async Task<IActionResult> Contact()
        {
            var httpClient = new HttpClient
            {
                BaseAddress = new Uri("https://localhost:5001")
            };
            httpClient.DefaultRequestHeaders.Clear();
            httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/vnd.enfi.hateoas+json")
                );

            var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
            ViewData["accessToken"] = accessToken;
            httpClient.SetBearerToken(accessToken);

            var res = await httpClient.GetAsync("api/posts").ConfigureAwait(false);
            if (res.IsSuccessStatusCode)
            {
                var json = await res.Content.ReadAsStringAsync().ConfigureAwait(false);
                var objects = JsonConvert.DeserializeObject<dynamic>(json);
                ViewData["json"] = objects;
                return View();
            }
            if (res.StatusCode == HttpStatusCode.Unauthorized)
            {
                return RedirectToAction("AccessDenied", "Authorization");
            }
            throw new Exception($"Error Occurred:${res.ReasonPhrase}");
        }
  • 添加 Authorization/AccessDenied
 public class AuthorizationController : Controller
    {
        public IActionResult AccessDenied()
        {
            return View();
        }
    }
@{ViewData["Title"] = "AccessDenied";}
<div class="container">
    <h2>Access Denied</h2>
</div>
  1. https://jwt.io 網(wǎng)站解析token
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容