IdentityServer4在asp.net core中注册UserService并从数据库中获取用户

2023-11-22

我已经搜索了所有关于如何注册UserService在asp.net core中使用IdentityServer4,但我似乎找不到正确的方法。

这是找到的注册InMemoryUsers的代码here,但是我想访问我的 MSSQL DB 中的用户,而不是示例中定义的静态用户。

var builder = services.AddIdentityServer(options =>
{
    options.SigningCertificate = cert;
});

builder.AddInMemoryClients(Clients.Get());
builder.AddInMemoryScopes(Scopes.Get());
builder.AddInMemoryUsers(Users.Get());

然后我看了看this这是为了身份服务器3.

var factory = new IdentityServerServiceFactory()
                .UseInMemoryClients(Clients.Get())
                .UseInMemoryScopes(Scopes.Get());

var userService = new UserService();
factory.UserService = new Registration<IUserService>(resolver => userService);

从在线阅读来看,我似乎需要使用 DI 系统来注册 UserService,但我不确定它如何绑定到 IdentityServer,例如。

services.AddScoped<IUserService, UserService>();

所以我的问题是:

我该如何绑定我的UserService给构建者(IdentityServer4 用户)?我将如何调用我的数据库来访问和验证我现有的数据库用户UserService(我使用存储库连接到数据库)?

考虑到这一点has跟...共事ASP.NET核心.

Thanks!


更新 - IdentityServer 4 已更改和替换用户服务 with IResourceOwnerPasswordValidator and IProfile服务

我用我的用户存储库从数据库中获取所有用户数据。这被注入(DI)到构造函数中,并在中定义Startup.cs。我还为身份服务器创建了以下类(也被注入):

首先定义ResourceOwnerPasswordValidator.cs:

public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
    //repository to get user from db
    private readonly IUserRepository _userRepository;

    public ResourceOwnerPasswordValidator(IUserRepository userRepository)
    {
        _userRepository = userRepository; //DI
    }

    //this is used to validate your user account with provided grant at /connect/token
    public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
    {
        try
        {
            //get your user model from db (by username - in my case its email)
            var user = await _userRepository.FindAsync(context.UserName);
            if (user != null)
            {
                //check if password match - remember to hash password if stored as hash in db
                if (user.Password == context.Password) {
                    //set the result
                    context.Result = new GrantValidationResult(
                        subject: user.UserId.ToString(),
                        authenticationMethod: "custom", 
                        claims: GetUserClaims(user));

                    return;
                } 

                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Incorrect password");
                return;
            }
            context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "User does not exist.");
            return;
        }
        catch (Exception ex)
        {
            context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid username or password");
        }
    }
    
    //build claims array from user data
    public static Claim[] GetUserClaims(User user)
    {
        return new Claim[]
        {
            new Claim("user_id", user.UserId.ToString() ?? ""),
            new Claim(JwtClaimTypes.Name, (!string.IsNullOrEmpty(user.Firstname) && !string.IsNullOrEmpty(user.Lastname)) ? (user.Firstname + " " + user.Lastname) : ""),
            new Claim(JwtClaimTypes.GivenName, user.Firstname  ?? ""),
            new Claim(JwtClaimTypes.FamilyName, user.Lastname  ?? ""),
            new Claim(JwtClaimTypes.Email, user.Email  ?? ""),
            new Claim("some_claim_you_want_to_see", user.Some_Data_From_User ?? ""),

            //roles
            new Claim(JwtClaimTypes.Role, user.Role)
        };
}

And ProfileService.cs:

public class ProfileService : IProfileService
{
    //services
    private readonly IUserRepository _userRepository;

    public ProfileService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    //Get user profile date in terms of claims when calling /connect/userinfo
    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        try
        {
            //depending on the scope accessing the user data.
            if (!string.IsNullOrEmpty(context.Subject.Identity.Name))
            {
                //get user from db (in my case this is by email)
                var user = await _userRepository.FindAsync(context.Subject.Identity.Name);

                if (user != null)
                {
                    var claims = GetUserClaims(user);

                    //set issued claims to return
                    context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();
                }
            }
            else
            {
                //get subject from context (this was set ResourceOwnerPasswordValidator.ValidateAsync),
                //where and subject was set to my user id.
                var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "sub");

                if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0)
                {
                    //get user from db (find user by user id)
                    var user = await _userRepository.FindAsync(long.Parse(userId.Value));

                    // issue the claims for the user
                    if (user != null)
                    {
                        var claims = ResourceOwnerPasswordValidator.GetUserClaims(user);

                        context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            //log your error
        }
    }

    //check if user account is active.
    public async Task IsActiveAsync(IsActiveContext context)
    {
        try
        {
            //get subject from context (set in ResourceOwnerPasswordValidator.ValidateAsync),
            var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "user_id");

            if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0)
            {
                var user = await _userRepository.FindAsync(long.Parse(userId.Value));

                if (user != null)
                {
                    if (user.IsActive)
                    {
                        context.IsActive = user.IsActive;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            //handle error logging
        }
    }
}

Then in Startup.cs我做了以下事情:

public void ConfigureServices(IServiceCollection services)
{
    //...

    //identity server 4 cert
    var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "idsrv4test.pfx"), "your_cert_password");

    //DI DBContext inject connection string
    services.AddScoped(_ => new YourDbContext(Configuration.GetConnectionString("DefaultConnection")));

    //my user repository
    services.AddScoped<IUserRepository, UserRepository>();

    //add identity server 4
    services.AddIdentityServer()
        .AddSigningCredential(cert)
        .AddInMemoryIdentityResources(Config.GetIdentityResources()) //check below
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddProfileService<ProfileService>();

    //Inject the classes we just created
    services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
    services.AddTransient<IProfileService, ProfileService>();

    //...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //...

    app.UseIdentityServer();

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    IdentityServerAuthenticationOptions identityServerValidationOptions = new IdentityServerAuthenticationOptions
    {
        //move host url into appsettings.json
        Authority = "http://localhost:50000/",
        ApiSecret = "secret",
        ApiName = "my.api.resource",
        AutomaticAuthenticate = true,
        SupportedTokens = SupportedTokens.Both,

        // required if you want to return a 403 and not a 401 for forbidden responses
        AutomaticChallenge = true,

        //change this to true for SLL
        RequireHttpsMetadata = false
    };

    app.UseIdentityServerAuthentication(identityServerValidationOptions);

    //...
}

您还需要Config.cs它定义了您的客户端、API 和资源。您可以在这里找到一个示例:https://github.com/IdentityServer/IdentityServer4.Demo/blob/master/src/IdentityServer4Demo/Config.cs

您现在应该能够调用 IdentityServer /connect/token

enter image description here

有关更多信息,请查看文档:https://media.readthedocs.org/pdf/identityserver4/release/identityserver4.pdf


旧答案(这确实not不再适用于较新的 IdentityServer4)

一旦你了解了事情的流程,事情就变得非常简单。

像这样配置你的 IdentityService (在 Startup.cs 中 -ConfigureServices()):

var builder = services.AddIdentityServer(options =>
{
    options.SigningCertificate = cert;
});

builder.AddInMemoryClients(Clients.Get());
builder.AddInMemoryScopes(Scopes.Get());

//** this piece of code DI's the UserService into IdentityServer **
builder.Services.AddTransient<IUserService, UserService>();

//for clarity of the next piece of code
services.AddTransient<IUserRepository, UserRepository>();

然后设置你的 UserService

public class UserService : IUserService
{
    //DI the repository from Startup.cs - see previous code block
    private IUserRepository _userRepository;

    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public Task AuthenticateLocalAsync(LocalAuthenticationContext context)
    {
        var user = _userRepository.Find(context.UserName);

        //check if passwords match against user column 
        //My password was hashed, 
        //so I had to hash it with the saved salt first and then compare.
        if (user.Password == context.Password)
        {
            context.AuthenticateResult = new AuthenticateResult(
                user.UserId.ToString(),
                user.Email,

                //I set up some claims 
                new Claim[]
                {
                    //Firstname and Surname are DB columns mapped to User object (from table [User])
                    new Claim(Constants.ClaimTypes.Name, user.Firstname + " " + user.Surname),
                    new Claim(Constants.ClaimTypes.Email, user.Email),
                    new Claim(Constants.ClaimTypes.Role, user.Role.ToString()),
                    //custom claim
                    new Claim("company", user.Company)
                }
            );
        }

        return Task.FromResult(0);
    }

    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        //find method in my repository to check my user email
        var user = _userRepository.Find(context.Subject.Identity.Name);

        if (user != null)
        {
            var claims = new Claim[]
                {
                    new Claim(Constants.ClaimTypes.Name, user.Firstname + " " + user.Surname),
                    new Claim(Constants.ClaimTypes.Email, user.Email),
                    new Claim(Constants.ClaimTypes.Role, user.Role.ToString(), ClaimValueTypes.Integer),
                    new Claim("company", user.Company)
            };

            context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type));
        }

        return Task.FromResult(0);
    }

    public Task IsActiveAsync(IsActiveContext context)
    {
        var user = _userRepository.Find(context.Subject.Identity.Name);

        return Task.FromResult(user != null);
    }
}

基本上都是通过注入UserService into builder(类型IdentityServerBuilder) Services,允许它在身份验证时调用 UserService。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

IdentityServer4在asp.net core中注册UserService并从数据库中获取用户 的相关文章

  • C++:无法使用scoped_allocator_adaptor传播polymorphic_allocator

    我有一个vector
  • 自动从 C# 代码进行调试过程并读取寄存器值

    我正在寻找一种方法来读取某个地址的 edx 注册表 就像这个问题中所问的那样 读取eax寄存器 https stackoverflow com questions 16490906 read eax register 虽然我的解决方案需要用
  • Signalr 在生产服务器中总是陷入长轮询

    当我在服务器中托管应用程序时 它会检查服务器端事件并始终回退到长轮询 服务器托管环境为Windows Server 2012 R1和IIS 7 5 无论如何 我们是否可以解决这个问题 https cloud githubuserconten
  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • C# 中值类型和引用类型有什么区别? [复制]

    这个问题在这里已经有答案了 我知道一些差异 值类型存储在堆栈上 而引用类型存储在托管堆上 值类型变量直接包含它们的值 而引用变量仅包含对托管堆上创建的对象位置的引用 我错过了任何其他区别吗 如果是的话 它们是什么 请阅读 堆栈是一个实现细节
  • C# 中可空类型是什么?

    当我们必须使用nullable输入 C net 任何人都可以举例说明 可空类型 何时使用可空类型 https web archive org web http broadcast oreilly com 2010 11 understand
  • App Insights 升级到 2.5 后,Azure 应用服务将无法启动

    我有一个存储库 它使用应用程序洞察来记录有关在 ASP NET 中运行的服务的信息 我们有一些 ASP NET Core 2 0 站点以及一些完整框架的 asp net 4 应用程序 随着应用程序洞察 2 5 的发布 我们在核心站点中获得了
  • 编译的表达式树会泄漏吗?

    根据我的理解 JIT 代码在程序运行时永远不会从内存中释放 这是否意味着重复调用 Compile 表达式树上会泄漏内存吗 这意味着仅在静态构造函数中编译表达式树或以其他方式缓存它们 这可能不那么简单 正确的 他们可能是GCed Lambda
  • 初始化变量的不同方式

    在 C 中初始化变量有多种方法 int z 3 与 int 相同z 3 Is int z z 3 same as int z z 3 您可以使用 int z z 3 Or just int z 3 Or int z 3 Or int z i
  • 网络参考共享类

    我用 Java 编写了一些 SOAP Web 服务 在 JBoss 5 1 上运行 其中两个共享一个类 AddressTO Web 服务在我的 ApplycationServer 上正确部署 一切都很顺利 直到我尝试在我的 C 客户端中使用
  • 检查 url 是否指向文件或页面

    我们需要以下内容 如果文件确实是文件 则从 URL 下载该文件 否则 如果它是一个页面 则什么也不做 举个简单的例子 我有以下命令来下载文件 My Computer Network DownloadFile http www wired c
  • 什么是 C 语言的高效工作流程? - Makefile + bash脚本

    我正在开发我的第一个项目 该项目将跨越多个 C 文件 对于我的前几个练习程序 我只是在中编写了我的代码main c并使用编译gcc main c o main 当我学习时 这对我有用 现在 我正在独自开展一个更大的项目 我想继续自己进行编译
  • 将应用程序从 Microsoft Access 迁移到 VB 或 C#.NET

    我目前正试图说服管理层需要将我们的应用程序之一移植到 NET 该应用程序已经发展成为 Access 中的一个庞然大物 SQL 后端 拥有 700 个链接表 650 个表单 子表单 130 个模块和 850 个查询 我几乎知道这样做的所有主要
  • 在 URL 中发送之前对特殊字符进行百分比编码

    我需要传递特殊字符 如 等 Facebook Twitter 和此类社交网站的 URL 为此 我将这些字符替换为 URL 转义码 return valToEncode Replace 21 Replace 23 Replace 24 Rep
  • 作为字符串的动态属性名称

    使用 DocumentDB 创建新文档时 我想设置属性名称动态地 目前我设置SomeProperty 像这样 await client CreateDocumentAsync dbs db colls x new SomeProperty
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • 在 ASP.NET 中将事件冒泡为父级

    我已经说过 ASP NET 中的层次结构 page user control 1 user control 2 control 3 我想要做的是 当控件 3 它可以是任何类型的控件 我一般都想这样做 让用户用它做一些触发回发的事情时 它会向
  • 窗体最大化时自动缩放子控件

    有没有办法在最大化屏幕或更改分辨率时使 Windows 窗体上的所有内容自动缩放 我发现手动缩放它是正确的 但是当切换分辨率时我每次都必须更改它 this AutoScaleDimensions new System Drawing Siz
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器

随机推荐

  • 将变量从 shell 脚本传递到 applescript

    我有一个 shell 脚本 我称之为使用osascript 然后osascript调用 shell 脚本并传入我在原始 shell 脚本中设置的变量 我不知道如何将该变量从 applescript 传递到 shell 脚本 如何将变量从 s
  • 如何将 ExifInterface 与流或 URI 结合使用

    我正在编写一个应用程序 可以从 Android 中的 共享方式 菜单发送照片 URI 您获得的 URI 类型是content media external images media 556然而ExifInterface想要一个标准的文件名
  • git rebase 删除提交

    我的 git 存储库中有以下更改历史记录 X Y Z A B C 我从代码 Z 的基础开始工作 并进行了 A B 和 C 三个更改 这些更改中的每一个都已作为单独的评论上传到 Gerrit 上 并且每个更改都取决于之前的更改 经过审查后 我
  • 视频元素在 IE8 中不显示?

    我想在所有浏览器中嵌入视频元素 但它在除 IE8 之外的所有浏览器中都可以正常工作 这里 我使用 mediaelement js 库来实现 首先 需要尝试以下几件事 确保 IE8 上安装了 Flash 这是旧版浏览器中 Mediaeleme
  • 无法在 Android Studio 中打开密钥库 - “找到冗余长度字节”

    我无法在 Android Studio 中打开现有的密钥库文件或使用jarsigner从命令行 在这两种情况下 错误消息都是 java security cert CertificateException 无法初始化 java io IOE
  • 如何让 AngularJS BLOB 下载 PDF?

    大家好 我对使用 AngularJS 进行开发非常陌生 我正在尝试弄清楚如何使用 BLOB 将 PDF 下载到本地计算机 我已经让它可以使用 JSON 现在我需要一个 PDF 我写了一些代码 但它似乎不起作用 html
  • 如何知道opencv中SSE2是否激活

    我有一个版本的 OpenCV 2 4 10 库 它是为 Windows 上的 Intel X64 构建的 我如何知道 CV SSE2 是否处于活动状态 我没有代码 我只有库 DLL 和标头 Thanks 您可以使用该功能检查SSE2是否启用
  • 如何在文档的 部分创建 Angular 指令?

    我是新来的angular js 我正在尝试创建一个指令来添加一些标题和元标记html 文档的一部分 但我遇到了一些麻烦 My index html文件如下
  • Rails 3 - Amazon S3 Paperclip EU 问题

    我在用着 Paperclip 2 3 16 Rails 3 0 9 Ruby 1 9 2 AWS S3 0 6 2 我正在尝试使用回形针上传到基于欧盟 爱尔兰 的存储桶 我的模型中有以下内容 has attached file image
  • 在其他项目中重用 Cucumber-JVM 步骤定义

    如何在其他项目中重用 Cucumber JVM 步骤定义来测试一些典型的 Web 操作 重点是 我创建了一些 java 项目 仅使用典型场景操作的步骤定义实现 例如 When I follow the link some link Then
  • Razor HTML 条件输出

    我有一个要作为主内容输出的项目列表 下面不包括主内容 每个项目都有 3 个属性 部分名称 标签和值 每个项目都包含在 a 中 每次部分名称更改时 我都必须打开 a 并关闭前一个 如果有的话 我正在使用带有以下代码的 Razor 视图 for
  • 保存为表单中的文件

    使用 Canvas 方法 toDataURL 我希望能够将图像保存在服务器端 使用 Rails 有了 toDataURL 字符串 如何在 HTML 表单中可以将其视为文件附件的表单中使用它 使用 jQuery Paperclip dataf
  • Intl.DateTimeFormat 返回超过 24 小时

    我有以下 Unix 时间戳 1611328500000 Fri Jan 22 2021 10 15 00 GMT 0500 Eastern Standard Time 我需要以韩国标准时间显示它 为此 我正在使用Intl DateTimeF
  • 在 Chrome 中拦截对 console.log 的调用

    我有一个无法更改的脚本 它会产生很多console log来电 我想添加另一层并在调用包含某些字符串时进行响应 这在 Firefox 中有效 但会抛出 Illegal invocation Chrome 中第 4 行出现错误 var old
  • 在 ERB 块中的字符串内包含 ERB 分隔符

    我正在编写一个显示代码以及输出的样式指南 目前它的结构使得代码只需要描述一次 并以原始版本和解释版本显示 如下所示
  • C/C++ 中的简单虚拟文件系统

    我想实现一个非常简单的虚拟文件系统 VFS 它支持一些基本的文件系统操作 例如 fwrite fopen fput 等 VFS 是某些具体操作系统之上的抽象层 例如Windows Linux 等 现在假设 fopen 界面看起来像这样 FI
  • 为什么 ORACLE 不允许在命令中使用连续的换行符?

    I write CREATE TABLE Person name CHAR 10 ssn INTEGER 并将其保存到文件 a sql 如果我然后通过在 SQL Plus 命令提示符中键入 a 来运行它 它会告诉我以 ssn 开头的行不被识
  • 将项目添加到地图列表中的 Kotlin 惯用方式

    我有一个MutableMap
  • 如何使用 ggplot2 绘制具有 (x,y,r,g,b) 坐标的图像?

    我有一个数据框image rgb 我已将图像的每个坐标的 r g b 值加载到其中 使用jpeg and reshape包 现在看起来像 gt head image rgb y x r g b 1 1 1 0 1372549 0 12549
  • IdentityServer4在asp.net core中注册UserService并从数据库中获取用户

    我已经搜索了所有关于如何注册UserService在asp net core中使用IdentityServer4 但我似乎找不到正确的方法 这是找到的注册InMemoryUsers的代码here 但是我想访问我的 MSSQL DB 中的用户