当这些存储在外部身份提供者服务中时与用户的关系

Posted

技术标签:

【中文标题】当这些存储在外部身份提供者服务中时与用户的关系【英文标题】:Relation to users when these are stored in an external Identity provider service 【发布时间】:2016-08-22 14:32:28 【问题描述】:

我正在尝试为它创建一个 API 和一个网站客户端。最近我阅读了很多关于 OAuth2 作为一种安全机制和提供身份验证服务的公司,例如 auth0.com 甚至 Azure Active Directory,我可以看到使用它们的优势

因为我习惯于始终将用户放在同一个数据库中,并且表与用户表的关系以一对多的形式存在,如下所示

public class User
    
        public string subjectId  get; set; 

        public virtual List<Invoice> Invoices  get; set; 
        /*
        More properties in here
        */
    

public class Invoice
    
        public int InvoiceId  get; set; 
        public string PaymentNumber  get; set; 
        public DateTime Date  get; set; 
        public double Amount  get; set; 
        public string Description  get; set; 

        public virtual User User  get; set; 
    

那么我的问题是。

如果用户存储在 Auth0.com 等外部认证服务中,

Invoice 类将如何处理与用户的关系? 是否只是在 Invoice 表中添加一个新属性 subjectId,这将采用身份验证服务分配的任何 id 的值?

在后一种情况下,Invoice 类会像下面这样吗?

public class Invoice
        
            public int InvoiceId  get; set; 
            public string PaymentNumber  get; set; 
            public DateTime Date  get; set; 
            public double Amount  get; set; 
            public string Description  get; set; 

            public string SubjectIdget;set;
        

另外,如果用户存储在其他地方,你如何进行查询,

Select * from Users u inner join Invoices i where Users.Name='John Doe' and i.Date>Somedate.

【问题讨论】:

每个 OAuth 提供商在成功验证后都会为您提供一个“userId”。用户 ID 在 OAuth 提供程序中是唯一的。所以你必须存储这两个信息:providername + userid。我通常创建一个至少包含 3 个字段的 [Users] 表:MyUserID(int auto-inc 或 guid 或 ...)、ProviderName(字符串)、ProviderUserID(字符串)。在您的情况下,我会将 MyUserID 放入您的发票中。 感谢您的回答,如果用户存储在外部服务中,您能告诉我如何进行查询,例如给我每个具有此名称且有发票的用户吗? "...至少 3 个字段" :) 我总是创建一个更丰富的表,其中包含从 OAuth 提供者那里收到的更多信息:名字、姓氏、电子邮件等。有时我会添加其他带有本地信息的字段。同样对于名字和姓氏,您可以决定是从 OAuth 提供商获取它们还是在第一次创建用户时在本地添加它们。 有道理,但是如果您使用外部身份服务器,您如何与 API 通信以将这些用户保存在更丰富的表中?,我正在设置的项目具有以下结构 MVC应用程序 -> API -> ID 服务器。您是否向 API 发出 http post 请求以保存这些用户? 第一个代码 sn-p 的第 8 行是错字吗? 【参考方案1】:

由于您提到 Auth0 作为您的身份提供者,因此有多种方法可以在您的数据库中实现用户表。 1. 使用 Auth0 验证/注册用户将发送带有 Profile Object 的响应,其中包含您需要的所有基本配置文件信息。将此配置文件对象发布回您自己的 API 以将其保存到数据库。此 API 端点应使用您收到的访问令牌以及来自 Auth0 的配置文件对象来保护。 2. 您可以在 Auth0 中创建一个自定义规则,将用户信息发布回您的 api。此规则在 Auth0 服务器上执行,因此这是一个安全调用。 3. 身份提供者(在我们的例子中为 Auth0)需要公开一个 API 端点,该端点为我们提供用户配置文件数据(例如:https://yourdoamin.auth0.com/userinfo)。您可以从 API 调用此端点以接收用户信息。

当用户注册到您的应用程序时,请使用其中一种技术在您的数据库中建立用户配置文件信息表。将身份提供者视为负责验证资源所有者(您的应用程序的用户)并提供访问令牌以安全访问您的 API/应用程序的服务始终是一个好主意。如果您的数据库中有用户的个人资料,则一旦用户通过身份验证,您就不必依赖身份提供者。

如果您有任何其他问题,请告诉我。

谢谢你, 索玛。

【讨论】:

我没有其他问题,您的回答很好。谢谢。我想我会选择选项 2,这样我就不必依赖客户端的开发人员将配置文件对象发布回 api 很高兴我能提供帮助。有大量关于如何在 Auth0 中编写规则的文档。谢谢-soma 可爱的方法!【参考方案2】:

我们的网站也有类似的设置。我们使用Passport 作为我们的用户数据库,而我们的网站根本没有用户表。这比在 Passport 和我们的网站之间拥有一堆重复数据要简单得多。我将使用我们的代码作为您正在执行的操作的示例,希望它有意义。

我们的网站有一个如下所示的 License 对象(Java 不是 C#,但它们很相似):

public class License 
  public String companyName;
  public List<User> users;

License 表如下所示(精简后):

CREATE TABLE licenses (
  id           UUID         NOT NULL,
  company_name VARCHAR(255) NOT NULL,
  PRIMARY KEY (id)
);

许可证通过这样的连接表识别与其关联的用户(Passport 使用 UUID 作为用户 ID,让生活再次变得简单):

CREATE TABLE users_licenses (
  users_id    UUID NOT NULL,
  licenses_id UUID NOT NULL,
  PRIMARY KEY (users_id, licenses_id),
  CONSTRAINT users_licenses_fk_1 FOREIGN KEY (licenses_id) REFERENCES licenses (id)
);

然后我们可以选择任一方向。如果我们知道用户 ID,我们可以像这样询问他们的所有许可证:

select * from licenses where users_id = ?

或者如果我们知道许可证 ID,我们可以询问所有有权访问许可证的用户:

select * from users_licenses where licenses_id = ?

一旦我们拥有一个或多个用户 ID,我们就可以调用 Passport /api/user 端点或 /api/user/search 端点来检索一个或多个用户对象。我们实际上使用的是 Passport Java 客户端 (https://github.com/inversoft/passport-java-client),它为我们调用 API,然后返回一个 List&lt;User&gt;。这是上面存储在License 类中的内容。该代码如下所示:

License license = licenseMapper.retrieveById(licenseId);
List<UUID> userIds = licenseMapper.retrieveUserIdsFor(licenseId);
ClientResponse<SearchResponse, Errors> clientResponse = passportClient.searchUsers(userIds);
license.users = clientResponse.successResponse.users;

LicenseMapper是一个MyBatis接口,执行SQL并返回License对象。 C# ORM 使用 LINQ,但类似。

这个设置的好处是我们的网站数据库中没有user 数据库表,我们必须保持同步。一切都是通过 API 从 Passport 加载的。我们也不关心性能。 Passport 是内部部署的,每秒可以进行数千次用户查找,因此我们总是加载数据而不是缓存数据。

当您搜索name='John Doe' 之类的任意用户时,唯一需要额外代码的问题是处理连接。处理此问题的唯一方法是首先查询您的用户数据库,检索所有 ID,然后加载他们的发票。如果您有一个大型用户数据库,这似乎很危险,但仍然可行。

在我们的情况下可能看起来像这样:

UserSearchCriteria criteria = new UserSearchCriteria().withName("John Doe");
ClientResponse<SearchResponse, Errors> clientResponse = passportClient.searchUsersByQueryString(criteria);
List<User> users = clientResponse.successResponse.users;
Set<License> licenses = new HashSet<>();
for (User user : users) 
  licenses.addAll(licenseMapper.retrieveByUserId(user.id));

【讨论】:

谢谢,这让我更清楚地了解其他人是如何实现这些东西的,这是我第一次处理外部身份验证服务器,我开始发现我的问题并不是唯一的,但是我想我要在我的 api 中添加一个用户表以及与其他实体的关系,这样我的代码将比为每个关系创建一个单独的表更简单

以上是关于当这些存储在外部身份提供者服务中时与用户的关系的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:在外部存储路径上创建目录 - 路径提供程序 getExternalStorageDirectory()

Linux进程与计划任务

放置在外部脚本文件中时 Javascript 不执行

在外部存储 Flyway 元数据表

conductor 事件处理程序

STM32怎么把程序存储在外部存储器执行