1. 程式人生 > >IdentityServer4授權和認證對接數據庫

IdentityServer4授權和認證對接數據庫

更新 info () 手動 class EDA sent bubuko refresh

接著上一篇講:https://www.cnblogs.com/nsky/p/10352678.html

我們之前都是用in-men的方式把數據添加到內存了,目的是為了測試方便,

現在我們把所有配置都添加到數據庫中

用IdeneityServer4 + EntityFramework + ASP.NET Identity的方式

上篇已經講過identity和profile的對接,這裏講講配置信息

之前都是這樣做的

技術分享圖片

添加包:IdentityServer4.EntityFramework

裏面有專門的2個DbContext管理對應的信息

分別是:PersistedGrantDbContext 和 ConfigurationDbContext

如果還記得Identity,裏面有個ApplicationDbContext

所以這裏總共有3個DbContext

ApplicationDbContext
PersistedGrantDbContext
ConfigurationDbContext

ApplicationDbContext - 負責涉及ASP.NET Identity的用戶所以表
dbo.AspNetRoleClaims
dbo.AspNetRoles
dbo.AspNetUserClaims
dbo.AspNetUserLogins
dbo.AspNetUserRoles
dbo.AspNetUsers
dbo.AspNetUserTokens

PersistedGrantDbContext - 負責存儲同意,授權代碼,刷新令牌和引用令牌
dbo.PersistedGrants

ConfigurationDbContext - 負責數據庫中剩余的所有其他內容

所以關於遷移,如果我更新任何AspNet Identity模型(即ApplicationUser),那麽我將在ApplicationDbContext上運行遷移。任何客戶端表或其他範圍都將在ConfigurationDbContext上運行。並且訪問entites(或表)將是相應的上下文。

添加包之後,添加依賴註入配置

 .AddConfigurationStore(options =>
            {
                options.ConfigureDbContext 
= builder => { builder.UseSqlServer(Configuration.GetConnectionString("conn"), sql => sql.MigrationsAssembly(migrationAssembly)); }; }) /* 這裏存儲的是,給用戶授權的token和一些授權信息 添加來自數據庫的操作數據(codes, tokens, consents) */ .AddOperationalStore(options => { options.ConfigureDbContext = builder => { builder.UseSqlServer(Configuration.GetConnectionString("conn"), sql => sql.MigrationsAssembly(migrationAssembly)); }; })
migrationAssembly是當前程序集;
var migrationAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

接下來生成migration,在項目的程序包管理控制臺輸入:
Add-Migration InitConfiguration -Context ConfigurationDbContext -o Date\Migrations\IdentityServer\ConfiguragtionDb
Add-Migration InitConfiguration -Context PersistedGrantDbContext -o Date\Migrations\IdentityServer\PersistedGrantDb
這樣就添加了兩個migration
技術分享圖片

執行update-database生成表
update-database -context ConfigurationDbContext

Update-Database -Context PersistedGrantDbContext
這樣就生成了表
技術分享圖片



因為現在還沒有界面錄入,可以先手動初始化配置文件 到數據庫,因為之前有config.cs
可以直接映射到model寫入數據庫
  /// <summary>
        /// 因為現在沒有通過UI去錄入api,client等信息
        /// 所有可以先init一些默認信息寫入數據庫
        /// </summary>
        /// <param name="app"></param>
        public void InitIdentityServerDataBase(IApplicationBuilder app)
        {
            //ApplicationServices返回的就是IServiceProvider,依賴註入的容器
            using (var scope = app.ApplicationServices.CreateScope())
            {
                //Update-Database
                scope.ServiceProvider.GetService<PersistedGrantDbContext>().Database.Migrate();

                //var provide = scope.ServiceProvider.GetService<PersistedGrantDbContext>();
                //ckk.PersistedGrants.Add(new IdentityServer4.EntityFramework.Entities.PersistedGrant {

                //});

                var configurationDbContext = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();

                /*
                 如果不走這個,
                 那麽應該手動執行 Update-Database -Context PersistedGrantDbContext
                 */
                configurationDbContext.Database.Migrate();

                if (!configurationDbContext.Clients.Any())
                {
                    foreach (var client in Config.GetClients())
                    {
                        //client.ToEntity() 會把當前實體映射到EF實體
                        configurationDbContext.Clients.Add(client.ToEntity());
                    }
                    configurationDbContext.SaveChanges();
                }
                if (!configurationDbContext.ApiResources.Any())
                {
                    foreach (var api in Config.GetApiResources())
                    {
                        configurationDbContext.ApiResources.Add(api.ToEntity());
                    }
                    configurationDbContext.SaveChanges();
                }
                if (!configurationDbContext.IdentityResources.Any())
                {
                    foreach (var identity in Config.GetIdentityResource())
                    {
                        configurationDbContext.IdentityResources.Add(identity.ToEntity());
                    }
                    configurationDbContext.SaveChanges();
                }
            }
        }

在Configure中調用

InitIdentityServerDataBase(app);

dotnet run就有數據了

客戶端不用配置,

獲取refresh_token,混合模式是支持refresh_token的

詳情:https://www.cnblogs.com/jesse2013/p/oidc-in-aspnetcore-with-identity-server.html

IdentityResource新增offline_access身份

技術分享圖片

AllowedScopes可以不用加: IdentityServerConstants.StandardScopes.OfflineAccess

服務端Client設置允許: AllowOfflineAccess = true,
客戶端配置:options.Scope.Add("offline_access");

技術分享圖片



拿到這個refresh_token,可以刷新access_token

技術分享圖片

一個refresh_token只能用一次,否則:
技術分享圖片

通過這個access_token就看訪問資源api了
但就算授權了。也只能訪問授權的api
identityResurce是資源信息,AllowedScopes設置了
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Profile,

就會返回這些信息,
OpenId是必須要的,因為OIDC要通過openid確認身份

Profile是可選的,只是客戶端默認是傳了Profile的,
因為oidc就是為了返回用信息而來的

Profile包含了一些基本的信息
name
family_name
given_name
middle_name
nickname
preferred_username
profile
picture
website
gender
birthdate
zoneinfo
locale
updated_at

Email包含了email 和 email_verified
這些從源碼可以看到:
https://github.com/IdentityServer/IdentityServer4/blob/63a50d7838af25896fbf836ea4e4f37b5e179cd8/src/Constants.cs

技術分享圖片


所以客戶端獲取的cliams是這些

技術分享圖片

因為profile是在我們的ProfileServices類返回的

那麽如果用戶沒有選擇


技術分享圖片


是不是應該不返回profile信息呢?

GetProfileDataAsync方法的context可以拿到當前請求的IdentityResources和ApiResource
技術分享圖片

好像通過 var claimTypes = context.RequestedClaimTypes;也可以判斷,選擇了profile,那麽RequestedClaimTypes是有值的

當然除了了一些基本信息外,客戶端還想獲取其他資源,這也是可以的

比如資源服務器有個接口,獲取額外的新,scope叫:OtherInfo

技術分享圖片


客戶端請求scope:options.Scope.Add("OtherInfo");
技術分享圖片

可以看到上面是身份信息,就是會返回的的用戶信息

下面是權限,就是可以去資源服務器獲取的那些權限,因為:access toke管的是權限

1:先不選擇,拿到是access_token裏面的scope是沒有OtherInfo的

這裏為啥有兩個offline_access?我也不知道

技術分享圖片

2:如果選擇的話。如果不出錯的話是有的

技術分享圖片

 

那麽資源服務器就可以根據當前這個scope來判斷了

 [HttpGet]
        public ActionResult GetOtherInfo()
        {
            //判斷是否授權
            var scope = User.Claims.FirstOrDefault(f => f.Type == "scope" && f.Value == "OtherInfo");
            if (scope != null)
            {
                return new JsonResult(
                    new
                    {
                        phone = "110",
                        address = "china",
                        age = "20",
                        gender = "m",
                        hobby = "coding"
                    }
                    );
            }
            else //禁止訪問
            {
                return BadRequest(StatusCodes.Status403Forbidden);
            }
        }

如果授權請求了OtherInfo請求是成功了

技術分享圖片


當沒有授權OtherInfo的時候,當然,返回什麽信息你可以自己定義
技術分享圖片

這說明你只有權限訪問資源服務器,授權了的api。

如果你的access_token是錯誤的。就說明你沒有授權,你都沒有機會進入api,會提示401

技術分享圖片

目前我想到的方法就是這樣,也許不是最好的

既然access_token包含profile信息,它又是JWT類型,那是不是可以DeCode呢?

答案是可以的:

技術分享圖片

界面輸出

技術分享圖片

參考:https://cloud.tencent.com/developer/article/1048128





IdentityServer4授權和認證對接數據庫