IdentityServer4 4.x版本 配置Scope的正確姿式

前言

IdentityServer4 是爲ASP.NET Core系列量身打造的一款基於 OpenID Connect 和 OAuth 2.0 認證的框架git

IdentityServer4官方文檔:https://identityserver4.readthedocs.io/github

看這篇文章前默認你對IdentityServer4 已經有一些瞭解。web

本篇使用IdentityServer4的4.x版本,跟老版本的稍微有些差異。下面直接進入正題。api

鑑權中心

建立IdentityServer4項目

使用IdentityServer4 來搭建一個鑑權中心,首先建議安裝一下IdentityServer4的官方項目模板。也能夠不安裝,本身建立項目,而後NuGet安裝須要的包也行。(不過仍是推薦用官方的模板,很方便)。app

命令行執行:dotnet new -i IdentityServer4.Templates框架

image-20200629205619088

安裝完成後會多出如下項目模板:asp.net

image-20200629205731577

我這裏選用is4inmem這個模板來建立項目,這個模板的數據都是寫死在內存中的,而且包含了Quickstart頁面,比較簡單方便。ide

來到個人項目目錄下執行:dotnet new is4inmem --name Idppost

image-20200701190325246

執行完成會生成如下文件:測試

image-20200701195853822

VS2019打開項目:

image-20200701195955107

運行項目:

image-20200701200225015

配置ApiResource、ApiScope、Clients

修改Startup:

// in-memory, code config
builder.AddInMemoryIdentityResources(Config.IdentityResources);
builder.AddInMemoryApiScopes(Config.ApiScopes);
//添加API資源
builder.AddInMemoryApiResources(Config.ApiResources);
builder.AddInMemoryClients(Config.Clients);

這裏比以前版本多了一個添加ApiScopes的方法:

builder.AddInMemoryApiScopes(Config.ApiScopes);

由於我接下來有要保護的API資源,因此須要添加一行:

builder.AddInMemoryApiResources(Config.ApiResources);

Config中的代碼:

public static class Config
{
    public static IEnumerable<IdentityResource> IdentityResources =>
        new IdentityResource[]
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
        };

    public static IEnumerable<ApiScope> ApiScopes =>
        new ApiScope[]
        {
            new ApiScope("scope1"),
            //new ApiScope("scope2"),
        };

    public static IEnumerable<ApiResource> ApiResources =>
        new ApiResource[]
        {
            new ApiResource("api1","#api1")
            {
                //!!!重要
                Scopes = { "scope1"}
            },
            //new ApiResource("api2","#api2")
            //{
            //    //!!!重要
            //    Scopes = { "scope2"}
            //},
        };

    public static IEnumerable<Client> Clients =>
        new Client[]
        {
            new Client
            {
                ClientId = "postman client",
                ClientName = "Client Credentials Client",

                AllowedGrantTypes = GrantTypes.ClientCredentials,
                ClientSecrets = { new Secret("postman secret".Sha256()) },

                AllowedScopes = { "scope1" }
            },
        };
}

我添加了一個ID爲postman client的客戶端,受權模式就用最簡單的ClientCredentials客戶端模式。須要注意的是4.x版本的ApiScope和ApiResource是分開配置的,而後在ApiResource中必定要添加Scopes。若是你在網上搜的IdentityServer4教程比較老的,都是沒有這個ApiScope的,默認ApiResource的Name做爲Scope。相似這樣:

public static IEnumerable<ApiResource> ApiResources =>
        new ApiResource[]
        {
            new ApiResource("api1","#api1"),//錯誤
            new ApiResource("api2","#api2"),//錯誤
        };


public static IEnumerable<Client> Clients =>
        new Client[]
        {
            new Client
            {
                ......

                AllowedScopes = { "api1", "api2" }
            },
        };

若是你這麼寫的話,雖然不影響你獲取token,可是你訪問api資源的話,永遠會獲得一個401錯誤!!!

ApiResource

下面添加一個api1資源,新建asp.netcore web應用並使用webapi模板:

image-20200701211036365

NuGet安裝:Microsoft.AspNetCore.Authentication.JwtBearer

Startup部分代碼:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            //IdentityServer地址
            options.Authority = "http://localhost:5001";
            //對應Idp中ApiResource的Name
            options.Audience = "api1";
            //不使用https
 		    options.RequireHttpsMetadata = false;
        });
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    //身份驗證
    app.UseAuthentication();

    //受權
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

給WeatherForecastController添加[Authorize]標記:

image-20200701214601854

運行Api1Resource,用postman測試訪問weatherforecast接口:

image-20200701214742071

此時獲得401錯誤。下面先去Idp獲取一個token:

image-20200701215031535

拿到token後再去訪問weatherforecast就沒問題了:

image-20200701215748634

進行到這裏,好像跟scope都沒什麼關係,那麼scope到底有什麼用處呢?

ApiScope策略受權

繼續修改代碼。

Api1Resource項目NuGet安裝:IdentityServer4.AccessTokenValidation

image-20200701221017612

再新建一個TestController用於區分:

image-20200701223359517

下面我須要作的是使用scope結合策略受權來分別限制TestController和WeatherForecastController的訪問權限。

修改Startup:

public void ConfigureServices(IServiceCollection services)
{
    ......

    services.AddAuthorization(options =>
    {
        //基於策略受權
        options.AddPolicy("WeatherPolicy", builder =>
        {
            //客戶端Scope中包含api1.weather.scope才能訪問
            builder.RequireScope("api1.weather.scope");
        });
        //基於策略受權
        options.AddPolicy("TestPolicy", builder =>
        {
            //客戶端Scope中包含api1.test.scope才能訪問
            builder.RequireScope("api1.test.scope");
        });
    });
}

爲了好理解,我把scope名稱分別改爲了:api1.weather.scope和api1.test.scope。

WeatherForecastController的Authorize標記修改一下:[Authorize(Policy = "WeatherPolicy")]

TestController的代碼很簡單:

image-20200701224046637

由於修改了scope名稱,須要把Idp中的scope名稱也改一下:

public static IEnumerable<ApiScope> ApiScopes =>
    new ApiScope[]
    {
        new ApiScope("api1.weather.scope"),
        new ApiScope("api1.test.scope"),
        //new ApiScope("scope2"),
    };

public static IEnumerable<ApiResource> ApiResources =>
    new ApiResource[]
    {
        new ApiResource("api1","#api1")
        {
            //!!!重要
            Scopes = { "api1.weather.scope", "api1.test.scope" }
        },
        //new ApiResource("api2","#api2")
        //{
        //    //!!!重要
        //    Scopes = { "scope2"}
        //},
    };

客戶端定義,AllowedScopes暫時只給一個api1.weather.scope測試一下

public static IEnumerable<Client> Clients =>
            new Client[]
            {
                new Client
                {
                    ClientId = "postman client",
                    ClientName = "Client Credentials Client",

                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    ClientSecrets = { new Secret("postman secret".Sha256()) },

                    AllowedScopes = { "api1.weather.scope" }
                },
            };

postman獲取token:

image-20200701225242813

訪問weatherforecast接口,正常響應200。

image-20200701225430395

再訪問test,獲得403錯誤:

image-20200701225508071

接下來修改一下Idp的客戶端定義,添加api1.test.scope:

AllowedScopes = { "api1.weather.scope", "api1.test.scope" }

修改Idp後必定要從新獲取token,jwt就是這樣,一旦生成就沒法改變。

image-20200701230022811

拿到新的token後訪問test和weatherforecast,這時候就均可以正常響應了。

image-20200701230107290

image-20200701230209695

總結

以上使用IdentityServer4搭建了一個鑑權中心,保護API資源,並使用ApiScope配合策略受權完成了一個簡單的權限控制。IdentityServer4的玩法很是多,知識點也不少。強烈推薦B站的@solenovex 楊老師的視頻,地址:https://www.bilibili.com/video/BV16b411k7yM 多看幾遍,會有收穫。。。

須要代碼的點這裏:https://github.com/xiajingren/IdentityServer4-4.x-Scope-Demo

相關文章
相關標籤/搜索