.NetCore IdentityServer4与EF实体结合
参考链接
- https://www.cnblogs.com/luckypc/p/10937598.html 根据现有的数据库生成 实体类
- https://www.cnblogs.com/stulzq/p/8726002.html IdentityServer 入门必看,下面的代码基本参照的都是这个文档
- https://www.cnblogs.com/jaycewu/p/7791102.html 详细介绍了如何使用。todo,这个还可以再看看权限认证的过滤器如何使用
运行环境:
.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