keycloak~aud受众字段的作用及如何生成
标识授权服务器在签发令牌时会将目标API的唯一标识写入aud字段明确告知客户端“这个令牌是发给谁用的”。验证API在收到令牌后必须验证aud字段的值是否与自身的标识如https://api.my-api.com或客户端ID相匹配。如果不匹配则必须拒绝该令牌。由于aud可能为字符串数组此时只需确保API自身的标识至少包含在其中一项即可。虽然aud验证很关键但它不能作为唯一的授权检查还应结合角色roles、权限范围scopes等其他声明进行多层授权校验。 在不同场景下的使用细节aud字段的具体使用在不同场景下有所不同访问令牌 (Access Token)aud标识该令牌可访问的目标API。在OAuth 2.0核心规范中令牌通常是“不透明白字符串”aud字段是随着JWT格式令牌的普及才变得常见。客户端通过audience或resource参数向授权服务器请求特定API的令牌。ID令牌 (ID Token)在基于OAuth 2.0的OpenID Connect中aud声明是必需REQUIRED字段。它必须包含客户端的client_id用于令牌接收方客户端确认令牌是“颁发给自己的”防止被其他客户端滥用。客户端断言 (Client Assertion)用于客户端向授权服务器证明自己身份。此场景下的JWT中的aud受众字段必须设为授权服务器的令牌端点URL如https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token。例如在客户端凭证模式中使用private_key_jwt认证方式时JWT断言的aud值就必须指向授权服务器的令牌端点。令牌内省 (Token Introspection)授权服务器的内省端点会返回令牌的元数据其中包含aud字段。资源服务器可以利用此信息对令牌进行更严格的验证例如确认aud是否包含自身的资源标识符。资源指示器 (RFC 8707)通过引入resource参数取代非标准的audience参数允许客户端在一个授权请求中为多个API请求单独的令牌避免产生一个能访问多项服务的“超级令牌”以此将潜在的安全风险降至最低。 常见配置误区在实际配置中一个常见的错误是混淆aud和client_id。例如将访问令牌的aud应指API错误地设置成了客户端的client_id这将导致API因无法匹配自身标识而拒绝令牌。keycloak中的aud如何生成位于org.keycloak.protocol.oidc.mappers.AudienceResolveProtocolMapper作用从当前用户的客户端角色中将客户端client_id提取出来放到aud字段里它是一个数组类型原码/** * Protocol mapper, which adds all client_ids of allowed clients to the audience field of the token. Allowed client means the client * for which user has at least one client role * * author a hrefmailto:mposoldaredhat.comMarek Posolda/a */ public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { private static final ListProviderConfigProperty configProperties new ArrayListProviderConfigProperty(); public static final String PROVIDER_ID oidc-audience-resolve-mapper; public ListProviderConfigProperty getConfigProperties() { return configProperties; } Override public String getId() { return PROVIDER_ID; } Override public String getDisplayType() { return Audience Resolve; } Override public String getDisplayCategory() { return TOKEN_MAPPER_CATEGORY; } Override public String getHelpText() { return Adds all client_ids of \allowed\ clients to the audience field of the token. Allowed client means the client\n for which user has at least one client role; } Override public int getPriority() { return ProtocolMapperUtils.PRIORITY_AUDIENCE_RESOLVE_MAPPER; } Override public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) { String clientId clientSessionCtx.getClientSession().getClient().getClientId(); for (Map.EntryString, AccessToken.Access entry : RoleResolveUtil.getAllResolvedClientRoles(session, clientSessionCtx).entrySet()) { // Dont add client itself to the audience if (entry.getKey().equals(clientId)) { continue; } AccessToken.Access access entry.getValue(); if (access ! null access.getRoles() ! null !access.getRoles().isEmpty()) { token.addAudience(entry.getKey()); } } return token; } public static ProtocolMapperModel createClaimMapper(String name) { ProtocolMapperModel mapper new ProtocolMapperModel(); mapper.setName(name); mapper.setProtocolMapper(PROVIDER_ID); mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); mapper.setConfig(Collections.emptyMap()); return mapper;