spring security 5 oauth2 资源服务器无法正确处理用户授权 报错insufficient_scope
现象
客户端通过授权码模式获取不透明令牌(opaque token),使用令牌访问资源服务器
资源服务器安全配置只能处理客户端scope授权,如果添加用户授权的判定规则,则报错
www-authenticate: Bearer error=“insufficient_scope”,error_description=“The request requires higher privileges than provided by the access token.”,error_uri=“https://tools.ietf.org/html/rfc6750#section-3.1”
原因
spring security 5 默认的令牌校验逻辑只处理scope,没有处理用户授权
源码
- 资源服务器不透明令牌默认配置
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerOpaqueTokenConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(OpaqueTokenIntrospector.class)
static class OpaqueTokenIntrospectionClientConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri")
NimbusOpaqueTokenIntrospector opaqueTokenIntrospector(OAuth2ResourceServerProperties properties) {
OAuth2ResourceServerProperties.Opaquetoken opaqueToken = properties.getOpaquetoken();
return new NimbusOpaqueTokenIntrospector(opaqueToken.getIntrospectionUri(), opaqueToken.getClientId(),
opaqueToken.getClientSecret());
}
}
- 内省和验证不透明令牌
org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector
private OAuth2AuthenticatedPrincipal convertClaimsSet(TokenIntrospectionSuccessResponse response) {
Collection<GrantedAuthority> authorities = new ArrayList<>();
Map<String, Object> claims = response.toJSONObject();
...
// 处理scope授权,加上前缀"SCOPE_"
if (response.getScope() != null) {
List<String> scopes = Collections.unmodifiableList(response.getScope().toStringList());
claims.put(OAuth2IntrospectionClaimNames.SCOPE, scopes);
for (String scope : scopes) {
authorities.add(new SimpleGrantedAuthority(this.authorityPrefix + scope));
}
}
// 授权中只包含scope,用户授权直接作为一般属性传递了
return new OAuth2IntrospectionAuthenticatedPrincipal(claims, authorities);
}
解决
- 自定义内省器
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.util.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;
import org.springframework.web.client.RestOperations;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Slf4j
public class DefaultOpaqueTokenIntrospector extends NimbusOpaqueTokenIntrospector {
public DefaultOpaqueTokenIntrospector(String introspectionUri, String clientId, String clientSecret) {
super(introspectionUri, clientId, clientSecret);
}
public DefaultOpaqueTokenIntrospector(String introspectionUri, RestOperations restOperations) {
super(introspectionUri, restOperations);
}
@Override
public OAuth2AuthenticatedPrincipal introspect(String token) {
OAuth2AuthenticatedPrincipal principal = super.introspect(token);
try {
List<String> userAuthorities = JSONUtils.to(principal.getAttribute("authorities"), List.class);
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.addAll(principal.getAuthorities());
for (String userAuthority : userAuthorities) {
authorities.add(new SimpleGrantedAuthority(userAuthority));
}
return new OAuth2IntrospectionAuthenticatedPrincipal(principal.getAttributes(), authorities);
} catch (ParseException e) {
log.warn("令牌用户授权信息解析失败", e);
}
return principal;
}
}
- 配置
@Bean
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri")
NimbusOpaqueTokenIntrospector opaqueTokenIntrospector(OAuth2ResourceServerProperties properties) {
OAuth2ResourceServerProperties.Opaquetoken opaqueToken = properties.getOpaquetoken();
return new DefaultOpaqueTokenIntrospector(opaqueToken.getIntrospectionUri(), opaqueToken.getClientId(),
opaqueToken.getClientSecret());
}
```
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2020-04-11 spring 启动时自动运行
2020-04-11 spring cloud oauth2授权服务 默认tokenService配置源码
2020-04-11 spring cloud 搭建oauth2授权服务 使用redis存储令牌
2020-04-11 spring cloud oauth2授权服务 clientDetails配置源码
2020-04-11 spring 验证框架
2020-04-11 IDEA 插件整理
2020-04-11 spring security笔记 默认登陆页面源码