IdentityServer4與API單項目整合(net core 3.X)

一、創建一個空的api項目

添加identityserver4的nuget包

 

 

 配置config文件

public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new IdentityResource[]
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            };
        }
        /// <summary>
        /// API資訊
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<ApiResource> GetApis()
        {
            return new[]
            {
                new ApiResource(IdentityServerConstants.LocalApi.ScopeName),
                new ApiResource("manageApi", "Demo API with Swagger")
            };
        }
        /// <summary>
        /// 客服端資訊
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            return new[]
            {
                new Client
                {
                    ClientId = "clientId",//客服端名稱
                    ClientName = "Swagger UI for demo_api",//描述
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword ,//指定允許的授權類型(AuthorizationCode,Implicit,Hybrid,ResourceOwner,ClientCredentials的合法組合)。
                    AllowAccessTokensViaBrowser = true,//是否通過瀏覽器為此客戶端傳輸訪問令牌
                    AccessTokenLifetime = 3600*24,
                    AuthorizationCodeLifetime=3600*24,
                    ClientSecrets ={new Secret("secret".Sha256())},
                    //RedirectUris =
                    //{
                    //    "//localhost:59152/oauth2-redirect.html"
                    //},
                    AllowedCorsOrigins = new string[]{ "//localhost:9012", "//101.133.234.110:21004",  "//115.159.83.179:21004" },
                    AllowedScopes = { "manageApi", IdentityServerConstants.LocalApi.ScopeName }//指定客戶端請求的api作用域。 如果為空,則客戶端無法訪問
                }
            };
        }

在Startup.cs中注入IdentityServer服務並使用中間件

 //配置身份伺服器與記憶體中的存儲,密鑰,客戶端和資源
            services.AddIdentityServer()
                   .AddDeveloperSigningCredential()
                   .AddInMemoryApiResources(config.GetApis())//添加api資源
                   .AddInMemoryClients(config.GetClients())//添加客戶端
                   .AddInMemoryIdentityResources(config.GetIdentityResources())//添加對OpenID Connect的支援
                     //使用自定義驗證
                    .AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator>()
                    .AddProfileService<ProfileService>();
//啟用IdentityServer
            app.UseIdentityServer();

接下來,我們去實現CustomResourceOwnerPasswordValidator這個類

創建一個CustomResourceOwnerPasswordValidator類,繼承IResourceOwnerPasswordValidator

 public class CustomResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
    {
        public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
            throw new NotImplementedException();
        }
    }

接下來,我們就是些驗證了

我們創建一個服務,去獲取資料庫裡面的用戶,驗證是否有當前登錄的用戶

這裡我們創建了一個userservice來驗證我們的輸入用戶是否存在

  public class UserService : IUserService
    {
        public async Task<TestPhoneUser> ValidateCredentials(string nameorphone, string password)
        {
            var dto= TestUser.FirstOrDefault(c => c.Phone == nameorphone && password == c.PassWord);
            if (dto != null)
                return dto;
            else
                return null;
        }
        public class TestPhoneUser
        {
            /// <summary>
            /// 唯一標識
            /// </summary>
            public int Id { get; set; }
            /// <summary>
            /// 手機號
            /// </summary>
            public string Phone { get; set; }
            /// <summary>
            /// 密碼
            /// </summary>
            public string PassWord { get; set; }
        }
        public List<TestPhoneUser> TestUser = new List<TestPhoneUser>
        {
            new TestPhoneUser
            {
                Id=1,
                Phone="12345678911",
                PassWord="123qwe"
            },new TestPhoneUser
            {
                Id=2,
                Phone="123",
                PassWord="123qwe"
            }
        };
        
    }

現在,我們去完善CustomResourceOwnerPasswordValidator

public class CustomResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
    {
        private readonly IUserService _userService;

        public CustomResourceOwnerPasswordValidator(IUserService userService)
        {
            _userService = userService;
        }
        public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
            var dto = await _userService.ValidateCredentials(context.UserName, context.Password);
            if (dto!=null)
            {
                context.Result = new GrantValidationResult(
                   dto.Id.ToString(),
                   "pwd",
                   DateTime.Now);
            }
            else
            {
                //驗證失敗
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "用戶名密碼錯誤");
            }
        }
    }

 再要配置profile

 public class ProfileService:IProfileService
    {
        public Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            context.IssuedClaims = context.Subject.Claims.ToList();
            return Task.CompletedTask;
        }

        public Task IsActiveAsync(IsActiveContext context)
        {
            context.IsActive = true;

            return Task.CompletedTask;
        }
    }

注入剛剛添加的服務

 services.AddScoped<IUserService, UserService>();
 services.AddScoped<IProfileService, ProfileService>();

在Startup.cs中加入identityServer驗證

 //用戶校驗
            services.AddLocalApiAuthentication();

            services.AddAuthorization(options =>
            {
                options.AddPolicy(IdentityServerConstants.LocalApi.PolicyName, policy =>
                {
                    policy.AddAuthenticationSchemes(IdentityServerConstants.LocalApi.AuthenticationScheme);
                    policy.RequireAuthenticatedUser();
                });
            });
            app.UseAuthentication();

postman測試

 

 

 反覆驗證了很多遍,都沒有問題(因為我之前core2.x的時候就是這樣寫的)

最後去identityserver官網,找到了問題所在

 

現在的資源都換成了apiscope

然後,我把ApiResource都換成了ApiScope然後再運行

 

ok,完美!!!(這個坑,坑了我一下午)

最後再給api添加驗證就行了

[Authorize(LocalApi.PolicyName)]

訪問api

 

訪問成功

參考文獻://identityserver4.readthedocs.io/en/latest/quickstarts/1_client_credentials.html#defining-an-api-scope