Spring安全与Sping Boot 3 -从安全上下文保持器获取JWT令牌

py49o6xq  于 2023-06-05  发布在  Spring
关注(0)|答案(2)|浏览(198)

在迁移到 Spring Boot 3之后,

extends ResourceServerConfigurerAdapter is deprecrated.

因此,无法使用重写方法

@Override
public void configure(ResourceServerSecurityConfigurer config) {
    config.tokenServices(tokenServices());
}

我可以用

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf().disable();
    http.authorizeHttpRequests(auth->auth.
            requestMatchers(whitelistedEndPoints()).permitAll()
            .anyRequest()
            .anonymous())
            .httpBasic().disable();
    return http.build();
}

我有一个现有的代码从OAuth获取jwt令牌作为

public String getJwtTokenFromAuth() {
    OAuth2Authentication auth =(OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
  
    OAuth2AuthenticationDetails oauth2AuthenticationDetails = (OAuth2AuthenticationDetails) auth.getDetails();
    return oauth2AuthenticationDetails.getTokenValue();
}

然而
OAuth2 Authentication和OAuth2 AuthenticationDetails不可用
如何将此代码/功能替换为Sping Boot 3的新Spring安全模块?下面是pom的依赖项,请建议我是否需要添加/删除任何?

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-resource-server</artifactId>
    </dependency>
unftdfkk

unftdfkk1#

Spring Security OAuth2已被弃用并删除了一段时间。替换是内置在Spring Security本身中的,要使用的依赖项正是您已经知道的:
所需的相关性为:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>

为了获得资源服务器中的令牌值,可以执行以下操作

class PrincipalIntrospector {
    
    public String token() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        JwtAuthenticationToken oauthToken = (JwtAuthenticationToken) authentication;
        return oauthToken.getToken().getTokenValue();
    }
    
}

为了确保在主体中正确配置角色,您可以使用以下内容:

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwt -> {
        List<String> authorities = jwt.getClaim("authorities");
        return authorities.stream().map(SimpleGrantedAuthority::new).collect(toList());
    });

    return jwtAuthenticationConverter;
}

为了解码并使spring验证你的jwt,你可以配置如下:

// used in case of public key
@Bean
public JwtDecoder jwtDecoder() {
    return NimbusJwtDecoder.withPublicKey(this.key).build();
}
// used in case of the private key
@Bean
public JwtDecoder jwtDecoder() {
    return NimbusJwtDecoder.withSecretKey(this.key).build();
}

Spring安全管道可以如下所示:

@Bean
public SecurityFilterChain filterChain(
        JwtDecoder  decoder,
        JwtAuthenticationConverter converter,
        HttpSecurity http) throws Exception {
    http.csrf().disable();
    http.authorizeHttpRequests(auth->auth.
                    requestMatchers(whitelistedEndPoints()).permitAll()
                    .anyRequest()
                    .authenticated())
            .oauth2ResourceServer().jwt().decoder(decoder).jwtAuthenticationConverter(converter);
    return http.build();
}
chy5wohz

chy5wohz2#

为什么IDE(和编译器)找不到OAuth2Authentication

OAuth2Authentication在2017年9月迁移到Spring Security 5时被删除。Spring Security 6(和5)中OAuth2的默认Authentication实现是:

  • 带有JWT解码器的JwtAuthenticationToken资源服务器(您的情况显然是因为您依赖于spring-security-oauth2-resource-server并期望JWT)
  • BearerTokenAuthentication用于资源服务器,具有访问令牌内省功能(在配置中称为opaqueToken,即使您可以内省JWT)
  • OAuth2AuthenticationToken客户端

我之所以写“default”是因为切换到资源服务器的其他Authentication实现非常容易(但这是另一个主题)。

在安全上下文中访问Authentication

或者:

  • 使用SecurityContextHolder静态访问器,就像您已经在做的那样
  • 让它作为@Controller方法参数“神奇地”注入(让框架在安全上下文保持器中检索Authentication的方便工具)

请注意,如果请求授权丢失或无效,则安全上下文中可能有AnonymousAuthenticationToken(您可以使用isAuthenticated()hasAuthority()等访问控制规则进行保护)。

在资源服务器中使用JWT解码器和默认Authentication实现的代码示例
var auth = SecurityContextHolder.getContext().getAuthentication();

if(auth instanceof JwtAuthenticationToken jwtAuth) {
    final var jwt = jwtAuth.getToken();
    final var bearerString = jwt.getTokenValue();
    ...
}
@RestController
public class MyController{
    @GetMapping("/public")
    public String mayGetTokenString(Authentication auth) {
        if(auth instanceof JwtAuthenticationToken jwtAuth) {
            return jwtAuth.getToken().getTokenValue();
        }
        return null;
    }

    @GetMapping("/authorized")
    @PreAuthorize("isAuthenticated()") 
    public String getTokenString(JwtAuthenticationtoken jwtAuth) {
        return jwtAuth.getToken().getTokenValue();
    } 
}

JwtAuthenticationToken可能需要替换为其他配置选项中的其他内容(内省、OAuth2客户端、返回自定义身份验证实现的身份验证转换器)

附言

如果使用Sping Boot 3发现OAuth2 / OpenID,您可能会发现my tutorials很有帮助,并解释了为什么@Valerio Vaudi的答案中的配置不是最佳的(在单个安全过滤器链中混合客户端和资源服务器配置绝对不是一个好主意)。

相关问题