我正在try 使用OAuth 2.0资源服务器JWT在一个简单的应用程序中使用JWT配置访问授权.

整个身份验证部分工作正常,但我在授权方面遇到了问题.即使令牌中存在正确的授权,所有受保护的端点也会给出403 Forbidden错误.

我try 使用默认作用域(SCOPE_)属性,并将配置更改为ROLES(ROLE_),但问题仍然存在.

有谁知道怎么解这个题吗?

Complete source code:https://github.com/GustavoSC1/spring-security-jwt

Example of generated token: eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJzcHJpbmctc2VjdXJpdHktand0Iiwic3ViIjoidXNlcm5hbWUiLCJleHAiOjE3MDU0NDMyOTQsImlhdCI6MTcwNTQwNzI5NCwicm9sZXMiOiJST0xFX0FETUlOIn0.peivwrtHx_7mr6eefqbiD5DplhFFzcVd7sCMmt3f7rk7Sk1i6KeRPQi5ubdvaEfnZSJQ6VKA5NAdltSbqidfzogmoIXjktfhsc5ZrNYyRhikVnWcWb3wRGdd1EZgIHALDFjXWXsyypauNjWdxZNiRKL93e6MG1uAo5pIy9p-9YP8JEr7O31wKDR1COSKzK3gQw42uecIB9H1rRlkx9pdk7Pf9RtfsSfCwc-NtViSMryCrecO9RiaLqFYdpdzeojiMcbqVEyBoqFhN2WoEpgDM8mR5zSdhGdQE1IVsIbfbCJ_0486ZuQiKsXP2kniljHL2b5qnaN07FJPVslK--Ccsg

SecurityConfig:

@Configuration
@EnableWebSecurity
//@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
  @Value("${jwt.public.key}")
  private RSAPublicKey key;
  @Value("${jwt.private.key}")
  private RSAPrivateKey priv;

  @Bean
  SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf(csrf -> csrf.disable())
        .authorizeHttpRequests(
            auth -> auth
                .requestMatchers("/authenticate").permitAll()
                .requestMatchers("/register").permitAll()
                .requestMatchers("/private").hasAnyRole("ADMIN"))
        .httpBasic(Customizer.withDefaults())
        
        // https://docs-spring-io.translate.goog/spring-security/reference/servlet/oauth2/resource-server/jwt.html?_x_tr_sl=en&_x_tr_tl=pt&_x_tr_hl=pt-BR&_x_tr_pto=sc
        .oauth2ResourceServer(
                conf -> conf.jwt(
                    jwt -> jwt.decoder(jwtDecoder())
                    .jwtAuthenticationConverter(jwtAuthenticationConverter())));
                
    return http.build();
  }
  
  @Bean
  public JwtAuthenticationConverter jwtAuthenticationConverter() {
      JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
      grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
      grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");

      JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
      jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
      return jwtAuthenticationConverter;
  }

  @Bean
  JwtDecoder jwtDecoder() {
    return NimbusJwtDecoder.withPublicKey(this.key).build();
  }

  @Bean
  JwtEncoder jwtEncoder() {
    JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();
    JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
    return new NimbusJwtEncoder(jwks);
  }
  
  @Bean
  PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
  
}

UserDetailsServiceImpl:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
  private final UserRepository userRepository;

  public UserDetailsServiceImpl(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    Optional<User> userOptional = userRepository.findByUsername(username);
    
    User user = userOptional.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
    
    return new UserAuthenticated(user.getUsername(), user.getPassword());
  }

}

UserAuthenticated:

public class UserAuthenticated implements UserDetails {
  
  private String username;
  private String password;

  public UserAuthenticated(String username, String password) {
    this.username = username;
    this.password = password;
  }

  @Override
  public String getUsername() {
    return username;
  }

  @Override
  public String getPassword() {
    return password;
  }

  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return List.of(() -> "ROLE_ADMIN");
  }

  @Override
  public boolean isAccountNonExpired() {
    return true;
  }

  @Override
  public boolean isAccountNonLocked() {
    return true;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return true;
  }

  @Override
  public boolean isEnabled() {
    return true;
  }

}

JwtService:

@Service
public class JwtService {
  private final JwtEncoder encoder;

  public JwtService(JwtEncoder encoder) {
    this.encoder = encoder;
  }

  public String generateToken(Authentication authentication) {
    Instant now = Instant.now();
    long expiry = 36000L;

    String scope = authentication
        .getAuthorities().stream()
        .map(GrantedAuthority::getAuthority)
        .collect(Collectors
            .joining(" "));

    JwtClaimsSet claims = JwtClaimsSet.builder()
        .issuer("spring-security-jwt")
        .issuedAt(now)
        .expiresAt(now.plusSeconds(expiry))
        .subject(authentication.getName())
        .claim("roles", scope)
        .build();

    return encoder.encode(
        JwtEncoderParameters.from(claims))
        .getTokenValue();
  }

}

PrivateController:

@RestController
@RequestMapping("private")
public class PrivateController {

  @GetMapping
  //@PreAuthorize("hasAuthority('ROLE_ADMIN')")
  public String getMessage() {
    return "Hello from private API controller";
  }
  
}

推荐答案

已生成所提供的令牌,并将roles字段设置为ROLE_ADMIN.在jwtAuthenticationConverter()中,您try 将setAuthorityPrefixROLE相加,结果是ROLE_ROLE_ADMIN.

要纠正这一点,请将该行修改为grantedAuthoritiesConverter.setAuthorityPrefix("");.

进行这一调整后,问题应该得到解决.如果您遇到任何进一步的问题,请告诉我.

NOTE:

如果省略此步骤,则默认前缀将为SCOPE,从而使您的角色变为SCOPE_ROLE_ADMIN.

Java相关问答推荐

从技术上讲,OPC UA客户端是否可以通过转发代理将请求通过 tunel 发送到OPC UA服务器?

取消按钮,但没有任何操作方法引发和异常

Hibernate EmptyInterceptor可以工作,但不能拦截器

使用Mockito进行的Junit测试失败

Java LocalTime.parse在本地PC上的Spring Boot中工作,但在Docker容器中不工作

Spring Data JPA慢慢地创建了太多非活动会话

测试何时使用Mockito强制转换对象会导致ClassCastException

Java17支持哪个MapR版本?

如何使用log4j2(Json)记录由";异常引起的所有";?

如何在太阳系模拟器中添加月球?

是否在settings.xml中使用条件Maven镜像?

Spring Boot中的应用程序.properties文件中未使用的属性

EXCEL中的公式单元格显示#NAME?

如何从HttpResponse实例获取Entity对象的内容?

为什么相同的数据条码在视觉上看起来不同?

根据应用程序 Select 的语言检索数据

如何使用Rascal Evaluator从编译的JAR访问Rascal函数?

如何使用外部函数从Java中获取C++ struct 的返回值&;内存API

Springboot应用程序无法识别任何@RestController或@Service,我认为@Repository也无法识别

message.acknowledge()没有';在使用Spring Boot在ActiveMQ中读取消息后,t将消息出列