我正在使用
Auth0
在我的 Spring Boot
后端进行身份验证。为了进行身份验证,我在 Auth0 中创建一个 API,从前端获取访问令牌,然后将其传递到后端。但是,在解码令牌后,我看到令牌中存在 permissions
,但它们不包含在 scope
部分中。
如果不将
permissions
包含在 scope
中,则以下 hasAuthority()
检查不起作用:
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/api/public").permitAll()
.requestMatchers("/api/private/**").authenticated()
.requestMatchers("/api/private-scoped/**").hasAuthority("SCOPE_read:advices"));
这阻止我使用
hasAuthority()
方法来正确检查 permissions
。
之前,有人建议(在此 StackOverflow 答案中)可以使用
rule
中的自定义 Auth0
将 permissions
包含在 scope
中,但由于 Auth0 中不再支持规则,因此该解决方案不再有效。
我的问题:
Auth0
中是否有新方法或配置来将权限包含在访问令牌的 scope claim
中?
如果没有,是否有其他方法可以处理访问令牌中的
permissions
,同时仍然能够在 Spring Security 中使用 hasAuthority()
或类似方法?
这个答案是关于在资源服务器端使用声明转换器解决问题,我认为这很常见。
请注意,您必须将转换器添加到您的
SecurityFilterChain
(见下文)。
索赔转换器
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class CustomClaimsConverter implements Converter<Jwt, AbstractAuthenticationToken> {
static final String SCOPE_PREFIX = "SCOPE_";
static final String CLAIM_PERMISSIONS = "permissions";
// this will convert scopes
private final JwtGrantedAuthoritiesConverter defaultGrantedAuthoritiesConverter =
new JwtGrantedAuthoritiesConverter();
@Override
public AbstractAuthenticationToken convert(@NonNull final Jwt source) {
return new JwtAuthenticationToken(
source,
Stream.concat(
defaultGrantedAuthoritiesConverter.convert(source).stream(),
extractPermissions(source)
).collect(Collectors.toSet())
);
}
private static Stream<? extends GrantedAuthority> extractPermissions(final Jwt jwt) {
Collection<String> permissions = jwt.getClaim(CLAIM_PERMISSIONS);
return permissions == null
? Stream.empty()
: permissions.stream()
.map(it -> SCOPE_PREFIX + it)
.map(SimpleGrantedAuthority::new);
}
}
测试
import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
class CustomClaimsConverterTest {
@Test
void convert_givenJwtWithPermissions_expectAuthenticationTokenWithScopes() {
var sut = new CustomClaimsConverter();
var authenticationToken =
sut.convert(createJwt());
assertNotNull(authenticationToken);
assertTrue(hasAuthorities(authenticationToken,
"SCOPE_openid", "SCOPE_profile", "SCOPE_email",
"SCOPE_read:advices", "SCOPE_write:advices"));
}
private static boolean hasAuthorities(
AbstractAuthenticationToken authenticationToken,
String... expectedAuth) {
return Arrays.stream(expectedAuth)
.allMatch(it -> authenticationToken.getAuthorities().contains(new SimpleGrantedAuthority(it)));
}
private static Jwt createJwt() {
return Jwt.withTokenValue("token")
.header("alg", "none")
.claim("scope", "openid profile email")
.claim("permissions", List.of("read:advices", "write:advices"))
.build();
}
}
SecurityFilterChain(仅相关部分)
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(new CustomClaimsConverter())))
.build();
}