资源是一个概念,可以划分为可以以各种方式实现的逻辑部分(范围)。无论哪种方式,尽管这在文档中可能不是很明显,但对于 IdentityServer,一个资源至少有一个范围。
为了允许客户端访问资源,必须配置客户端允许该客户端的范围。在示例中,范围的名称等于资源的名称,但实际上一个资源可能具有多个范围。请注意,仅允许使用一个范围的客户端可以访问整个资源(受众),除非资源 (api) 根据允许的范围过滤访问(范围是作为范围声明的访问令牌的一部分)。
IdentityServer 的一个特点是它是一个令牌提供者,例如用于访问令牌和身份令牌。
从最后一个开始,有两种类型的身份令牌。有一个“最小”身份令牌(仅包含“子”声明)仅在请求时与访问令牌一起发布。并且客户端可以在 UserInfo 端点请求一个“完整”身份令牌。
对于 IdentityServer,用户声明是(身份信息的)资源。附带说明一下,在使用此信息之前应征得用户同意,因为用户拥有此信息。
并非所有用户声明都应发送到在 UserInfo 端点请求的身份令牌。为了确定应该使用哪些声明,IdentityServer 查看 Identity~ 表。基于这些表,RequestedClaims 集合可用于 UserInfo 上下文,用于过滤用户声明资源。
换句话说,当客户端配置为请求“openid”范围时,则仅包含“sub”声明,添加“profile”范围将包含基础配置文件声明(如果在 UserClaims 表中可用) ,并且配置“电子邮件”范围会将电子邮件也添加到身份令牌中。
对于访问令牌也有类似的机制。基于 ApiClaims 和 ApiScopeClaims 表,RequestedClaims 集合可用于授权上下文。请查看my answer here 了解更多信息。
假设您想将“名称”声明添加到访问令牌,然后将其添加到其中一个表中。查看my answer here 或者实际上是整个线程,了解更多信息。
虽然 IdentityServer 可以向访问令牌添加各种声明,但您应该想知道这是否是正确的方法,如 this post 中所述。
访问令牌声明的问题在于这些声明用于用户授权。但是用户声明通常并不意味着用户授权。而且您不希望最终得到一个包含所有声明的超级访问令牌。
退一步,IdentityServer 是用来配置资源保护和客户端授权的。客户端可以连接到资源(范围),但有权访问信息的是用户:来自客户端的请求始终“代表用户”。这就是为什么“子”声明总是(在交互式流程中)访问令牌的一部分。此外,可以扩展 IdentityServer 以处理用户身份验证。但是,尽管在当前设计中是可能的,但用户授权不是(或至少不再是)IdentityServer 的关注点。
IdentityServer 的创建者过去一直在考虑用户授权,并为此提出了一个单独的服务:PolicyServer。将授权声明从 IdentityServer 和访问令牌中取出。
因此,为了回答您的问题,我们使用不同的表来配置用于构建令牌的资源、范围、客户端和过滤器。用户声明讲述了有关用户(身份)的一些信息,并且不在访问令牌中。
对于用户授权,我可以推荐一个类似 PolicyServer 服务器的实现,结合基于资源的授权。
表格的简短摘要:
AspNetUserClaims - 用户信息资源
ApiResources - 资源的名称
ApiScopes - 作为资源一部分的一对多范围
IdentityResources - 实际上是身份的范围(例如 openid 配置文件)
IdentityClaims - 为身份令牌过滤 AspNetUserClaims
ApiClaims - 为访问令牌过滤 AspNetUserClaims,无论请求哪个范围
ApiScopeClaims - 过滤访问令牌的 AspNetUserClaims,取决于请求的范围
ClientClaims - 为特定客户包含的声明
ClientScopes - 客户端允许的范围
客户端请求范围。如果省略范围,则(根据规范)所有允许的范围都被视为请求。
API 是资源(受众)的一部分。当资源具有多个范围时,需要更细粒度的客户端授权(基于范围)。
用户授权用于确定允许用户做什么:客户端可以连接到 api,按范围(例如日历)过滤,并且用户有权读取但不能写入事件。
虽然 calendar.write 和 calendar.read 可以是作用域,但与用户授权无关。用户可以被授权读取和写入,但由于客户端可能受到限制(例如 calendar.read),用户可能必须使用单独的客户端才能连接到资源,例如一个日历应用程序两者都有,一个电子邮件应用程序只有 calendar.read。