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