.NetCore IdentityServer4与EF实体结合

参考链接

运行环境:
.Net Core 3.0
ef 3.0

1.安装Identity,简单的开启控制权限

Nuget包

目录说明

2.根据现有的数据库生成 实体类

参考链接:https://www.cnblogs.com/luckypc/p/10937598.html

如何在vs中打开 Nuget包管理器
工具-Nuget包管理器-程序包管理器控制台

生成代码

Scaffold-DbContext "Data Source=.;Initial Catalog=OA;User ID=sa;Password=123456" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Force

3.将Identity 与 EF 绑定结合

IdentityServerConfig 代码

public class IdentityServerConfig
    {
        //添加Config.cs配置IdentityResource
        public static IEnumerable<IdentityResource> GetIdentityResourceResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(), //必须要添加,否则报无效的scope错误
                new IdentityResources.Profile()
            };
        }



        /// <summary>
        /// 添加api资源
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<ApiResource> GetResources()
        {
            return new List<ApiResource>
            {

                new ApiResource("api1","My Api",new List<string>(){ "userName", "userCode"})
            };
        }
        /// <summary>
        /// 添加客户端,定义一个可以访问此api的客户端
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
                {
                    new Client
                    {
                        ClientId = "client",
                       //需要账户密码模式
                        AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                       
                        // 用于认证的密码
                        ClientSecrets =
                        {
                            new Secret("1234554".Sha256())
                        },
                           AllowedScopes = { "api1",IdentityServerConstants.StandardScopes.OpenId, //必须要添加,否则报forbidden错误
                  IdentityServerConstants.StandardScopes.Profile }
                    }

                };

        }
    }

CustomResourceOwnerPasswordValidator 自定义验证器,实现IResourceOwnerPasswordValidator接口

	/// <summary>
	/// 自定义 Resource owner password 验证器
	/// </summary>
	public class CustomResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
	{

		private readonly OAContext dbContext=new OAContext();//EF上下文

		/// <summary>
		/// 验证
		/// </summary>
		/// <param name="context"></param>
		/// <returns></returns>
		public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
		{
			BaseUser user = dbContext.BaseUser.FirstOrDefault(a => a.Code.Equals(context.UserName) && a.Password.Equals(context.Password));
			//此处使用context.UserName, context.Password 用户名和密码来与数据库的数据做校验
			if (user!=null)
			{
			

				//验证通过返回结果 
				//subjectId 为用户唯一标识 一般为用户id
				//authenticationMethod 描述自定义授权类型的认证方法 
				//authTime 授权时间
				//claims 需要返回的用户身份信息单元 此处应该根据我们从数据库读取到的用户信息 添加Claims 如果是从数据库中读取角色信息,那么我们应该在此处添加
				context.Result = new GrantValidationResult(
					user.Id.ToString(),
					OidcConstants.AuthenticationMethods.Password, DateTime.Now,
					user.Claims);
			}
			else
			{
				//验证失败
				context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential");
			}
			return Task.CompletedTask;
		}
	}

CustomProfileService 实现IProfileService接口

	//IdentityServer提供了接口访问用户信息,但是默认返回的数据只有sub,就是上面设置的subject: context.UserName,要返回更多的信息,需要实现IProfileService接口
	public class CustomProfileService : IProfileService
	{
		private readonly OAContext dbContext = new OAContext();//EF上下文
		/// <summary>
		/// 只要有关用户的身份信息单元被请求(例如在令牌创建期间或通过用户信息终点),就会调用此方法
		/// </summary>
		/// <param name="context">The context.</param>
		/// <returns></returns>
		public virtual Task GetProfileDataAsync(ProfileDataRequestContext context)
		{
			//2020.04.14 不考虑验证

			//context.Subject.Claims就是之前实现IResourceOwnerPasswordValidator接口时claims: user.Claims给到的数据。
			//另外,经过调试发现,显示执行ResourceOwnerPasswordValidator 里的ValidateAsync,然后执行ProfileService 里的IsActiveAsync,GetProfileDataAsync。
			var claims = context.Subject.Claims.ToList();
			//set issued claims to return
			context.IssuedClaims = claims.ToList();

			return Task.CompletedTask;
		}

		/// <summary>
		/// 验证用户是否有效 例如:token创建或者验证
		/// </summary>
		/// <param name="context">The context.</param>
		/// <returns></returns>
		public virtual Task IsActiveAsync(IsActiveContext context)
		{
			//2020.04.14 不考虑验证
			//Logger.LogDebug("IsActive called from: {caller}", context.Caller);
			//string userID = context.Subject.GetSubjectId();
			
			//var user = dbContext.BaseUser.FirstOrDefault(a => a.Id.Equals(Convert.ToInt32(userID)));
			//context.IsActive = user?.Effective == true;
			context.IsActive =  true;
			return Task.CompletedTask;
		}
	}

Startup 配置

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();


            services.AddIdentityServer()
            .AddInMemoryApiResources(IdentityServerConfig.GetResources())//添加配置的api资源
              .AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResourceResources()) //.自定义身份资源资源 ,暂时没有啥效果
            .AddInMemoryClients(IdentityServerConfig.GetClients())//添加客户端,定义一个可以访问此api的客户端
               .AddProfileService<CustomProfileService>()// 添加自定义 claim
             .AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator>()//过滤器验证用户
            .AddDeveloperSigningCredential();


            services.AddAuthentication("Bearer")
               .AddIdentityServerAuthentication(options =>
               {
                   options.Authority = "http://localhost:5000";
                   options.RequireHttpsMetadata = false;
                   options.ApiName = "api1";
               });
        }


        // 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.UseIdentityServer();
            app.UseAuthentication();//开启权限认证

            app.UseRouting();

            app.UseAuthorization();

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

TestController 测试使用的控制器

在控制器中,可以用这个得到context.FirstOrDefault(a=>a.Type=="userName").Value 对应的自定义信息

  [Route("test/[action]")]
    [ApiController]
   
    public class TestController : ControllerBase
    {
        // GET: api/Test
        [HttpGet]
        [Authorize]
        public IActionResult Get()
        {
            IEnumerable<System.Security.Claims.Claim> context = HttpContext.User.Claims;
            return new JsonResult(from c in HttpContext.User.Claims select new { c.Type, c.Value });
        }
        [HttpGet]
        public IEnumerable<string> Get2()
        {
            return new string[] { "阿萨德", "value啊啊2" };
        }

     
        // POST: api/Test
        [HttpPost]
        public void Post([FromBody] string value)
        {
        }

    }

调用这个接口,可以得到自定义返回的值 http://localhost:5000/connect/userinfo
参考链接:https://www.cnblogs.com/jaycewu/p/7791102.html todo,这个还可以再看看权限认证的过滤器如何使用

PostMan测试说明

请求Token

post地址:http://localhost:5000/connect/token
form-data参数:
    client_id    client
    client_secret    1234554
    grant_type    password
    username    你的用户名
    password    你的密码

访问API控制器,需要带上token,否则401报错,没有权限

http://localhost:5000/test/get

根据Token获取用户信息

http://localhost:5000/connect/userinfo
原文地址:https://www.cnblogs.com/Alex-Mercer/p/12698025.html