如何在 Spring Security 的访问令牌的范围声明中包含 Auth0 权限?

问题描述 投票:0回答:1

我正在使用

Auth0
在我的
Spring Boot
后端进行身份验证。为了进行身份验证,我在 Auth0 中创建一个 API,从前端获取访问令牌,然后将其传递到后端。但是,在解码令牌后,我看到令牌中存在
permissions
,但它们不包含在
scope
部分中。

enter image description here

如果不将

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()
或类似方法?

spring-boot permissions scope access-token auth0
1个回答
0
投票

这个答案是关于在资源服务器端使用声明转换器解决问题,我认为这很常见。

请注意,您必须将转换器添加到您的

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();
    }
© www.soinside.com 2019 - 2024. All rights reserved.