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

Gen*_*ric 6 oauth azure oauth-2.0 asp.net-identity asp.net-web-api2

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

因为我以前总是让用户在同一个数据库和表中,以一对多的形式与Users表有关系,如下所示

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; }
    }
Run Code Online (Sandbox Code Playgroud)

我的问题是.

如果用户存储在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 SubjectId{get;set;}
        }
Run Code Online (Sandbox Code Playgroud)

此外,如果用户存储在其他地方,您如何进行查询,如:

Select * from Users u inner join Invoices i where Users.Name='John Doe' and i.Date>Somedate.
Run Code Online (Sandbox Code Playgroud)

Som*_*dda 5

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

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

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

谢谢你,索玛。


Bri*_*lli 5

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

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

public class License {
  public String companyName;
  public List<User> users;
}
Run Code Online (Sandbox Code Playgroud)

许可证表如下所示(精简):

CREATE TABLE licenses (
  id           UUID         NOT NULL,
  company_name VARCHAR(255) NOT NULL,
  PRIMARY KEY (id)
);
Run Code Online (Sandbox Code Playgroud)

许可证通过像这样的连接表来标识与其关联的用户(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)
);
Run Code Online (Sandbox Code Playgroud)

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

select * from licenses where users_id = ?
Run Code Online (Sandbox Code Playgroud)

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

select * from users_licenses where licenses_id = ?
Run Code Online (Sandbox Code Playgroud)

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

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

LicenseMapper是一个执行 SQL 并返回 License 对象的 MyBatis 接口。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));
}
Run Code Online (Sandbox Code Playgroud)