Spring Authorization Server:如何为各授权类型配置不同Scope?
问题
我想要自定义授权类型mfa,流程如下:用户需先获取带有mfa_required scope的访问令牌,再使用该令牌通过mfa授权类型获取另一访问令牌,后者具备write、read等可访问资源服务器的scope。
当前实现需要创建两个RegisteredClient:
第一个用于获取带mfa_required的令牌:
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("cheems_doge") .clientSecret("very_secret_wow") .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .redirectUri("http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc") .scope("mfa_required") // 该令牌仅可用于mfa授权类型 .clientSettings( ClientSettings.builder() // 移除同意页 .requireAuthorizationConsent(false) .requireProofKey(true) .build()) .build();
第二个用于通过mfa授权类型获取资源访问令牌:
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("cheems_doge_2") .clientSecret("very_secret_wow_2") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) .authorizationGrantType(CustomGrantType.MFA) .redirectUri("http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc") .scope("write read etc..") // 该令牌可访问资源服务器 .clientSettings( ClientSettings.builder() .requireAuthorizationConsent(true) .requireProofKey(false) .build()) .build();
但目前无法在单个RegisteredClient中为不同授权类型配置不同Scope,只能创建两个RegisteredClient,导致客户端需存储两个client_id。请问是否存在为每种授权类型单独配置Scope的方法?
解决方案
Spring Security OAuth2默认的RegisteredClient模型不支持直接为每个授权类型绑定专属Scope,但可以通过以下两种方式实现类似效果,避免维护多个客户端:
1. 自定义令牌颁发逻辑
在授权服务器的令牌端点处理器中,根据当前使用的授权类型动态过滤可颁发的Scope:
- 先在单个
RegisteredClient中配置所有需要的Scope(mfa_required、write、read等) - 自定义
OAuth2TokenCustomizer或者扩展AuthorizationTokenServices,在颁发令牌时做如下处理:- 若当前授权类型是
authorization_code/client_credentials,仅保留mfa_requiredScope - 若当前授权类型是自定义的
mfa,仅保留write、read等资源访问Scope
- 若当前授权类型是
示例代码片段:
@Bean public OAuth2TokenCustomizer<JwtEncodingContext> jwtTokenCustomizer() { return context -> { AuthorizationGrantType grantType = context.getAuthorizationGrantType(); Set<String> allowedScopes = new HashSet<>(); if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(grantType) || AuthorizationGrantType.CLIENT_CREDENTIALS.equals(grantType)) { allowedScopes.add("mfa_required"); } else if (CustomGrantType.MFA.equals(grantType)) { allowedScopes.addAll(Set.of("write", "read")); } context.getClaims().claim("scope", String.join(" ", allowedScopes)); // 更新令牌的scope集合 context.getTokenContext().setScopes(allowedScopes); }; }
2. 扩展RegisteredClient模型
如果需要更严谨的配置隔离,可以自定义RegisteredClient的存储逻辑:
- 扩展默认的
RegisteredClient实体,添加grantTypeScopes映射字段,存储每个授权类型对应的Scope列表 - 自定义
RegisteredClientRepository或RegisteredClientService,查询客户端时返回包含自定义映射的实例 - 在授权流程中,从自定义的
RegisteredClient中获取当前授权类型对应的有效Scope,替代默认的全局Scope集合
这种方式配置更清晰,但需要修改客户端存储的底层实现,适合对权限控制有严格要求的场景。
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

