为什么这个 API 对于拥有包含 "correct" 权限的 JWT 令牌的用户是禁止的?

Why this API is forbidden for an user having a JWT token containing the "correct" authority?

我不太喜欢 Spring 安全和 JWT 令牌,我对我正在从事的项目有以下疑问。

基本上我有这个 SecurityConfiguration class 包含我的 Spring 安全配置,如您所见,它旨在处理 JWT 令牌:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    @Autowired
    @Qualifier("customUserDetailsService")
    private UserDetailsService userDetailsService;
    
    @Autowired
    private JwtConfig jwtConfig;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    private static final String[] USER_MATCHER = { "/api/user/email/**"};
    private static final String[] ADMIN_MATCHER = { "/api/users/email/**"};
    
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
    {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
    {
        return super.authenticationManagerBean();
    }   
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        /*
         * NOTE:
         * Using hasRole expects the authority names to start with 'ROLE_' prefix
         * Instead, we use hasAuthority as we can use the names as it is
         */
        http.csrf().disable()
                   .authorizeRequests()
                   //.antMatchers(USER_MATCHER).hasAnyAuthority("USER")
                   .antMatchers(USER_MATCHER).hasAnyAuthority("CLIENT")
                   .antMatchers(ADMIN_MATCHER).hasAnyAuthority("ADMIN")
                   .antMatchers("/api/users/test").authenticated()
                   .antMatchers(HttpMethod.POST, jwtConfig.getUri()).permitAll()
                   .anyRequest().denyAll()
                   .and()
                   .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(
            new TokenVerificationFilter(authenticationManager(), jwtConfig, jwtTokenUtil),UsernamePasswordAuthenticationFilter.class);
    }
    
    /* To allow Pre-flight [OPTIONS] request from browser */
    @Override
    public void configure(WebSecurity web) 
    {
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder()
    {
        return new BCryptPasswordEncoder();
    };

    
}

正如您在前面的代码中看到的,我有以下两个匹配器列表:

private static final String[] USER_MATCHER = { "/api/user/email/**"};
private static final String[] ADMIN_MATCHER = { "/api/users/email/**"};

目前两者包含相同的 API 路径:/api/users/email/**。这是因为我最初的想法是这个 API 应该对简单用户和管理员用户可用。

然后在代码中可以根据生成的令牌中包含的权限找到以下匹配器定义:

.antMatchers(USER_MATCHER).hasAnyAuthority("CLIENT")
.antMatchers(ADMIN_MATCHER).hasAnyAuthority("ADMIN")

(USER_MATCHERCLIENT 权限有关,目前是最简单的操作类型可以执行...请不要太在意权限名称...这些主要是一些示例,然后我会更好地定义我的权限列表)。

以这种方式进行操作,我预计必须为具有 ADMIN 的用户启用此 /api/users/email/ API 权限,但也适用于具有 CLIENT 权限的用户。

不过好像不对,举个例子。我为具有 ADMIN 权限的用户生成令牌,如下所示:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ4eHhAZ21haWwuY29tIiwibmFtZSI6IlJlbmF0byBudWxsIEdpYWxsaSIsInVzZXJfcHJvZmlsZXMiOlsiQURNSU4iXSwiZXhwIjoxNjQwMjg2NTY5LCJpYXQiOjE2NDAyMDAxNjksImF1dGhvcml0aWVzIjpbIkFETUlOIl19.WQGYKbo_ihrV2Nu1RlxrCweBpgU-Y-dNh9L6R9vrj3vhTyvbPlsyzWNPe8ljtP6WZ8_Vvv8FUDJIa6y5BLS1SA

使用 https://jwt.io/ 网站可以看到此令牌具有 ADMIN 权限:

{
  "sub": "xxx@gmail.com",
  "name": "Renato null Gialli",
  "user_profiles": [
    "ADMIN"
  ],
  "exp": 1640286569,
  "iat": 1640200169,
  "authorities": [
    "ADMIN"
  ]
}

所以我使用这个标记来调用我的目标 API (/api/users/email/) 我得到了我所期望的:

好的,现在我为我的系统的另一个用户生成了一个全新的 JWT 令牌,该用户只有 CLIENT 权限。它生成如下内容:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ5eXlAZ21haWwuY29tIiwibmFtZSI6Ik1hcmlvIG51bGwgUm9zc2kiLCJ1c2VyX3Byb2ZpbGVzIjpbIkNMSUVOVCJdLCJleHAiOjE2NDAyODY5MzEsImlhdCI6MTY0MDIwMDUzMSwiYXV0aG9yaXRpZXMiOlsiQ0xJRU5UIl19.MYM6J3bGSK2PJvxPpi01BHjbcyOiONegYlbZ--lfEtg3p6Hw91acyKYC7KADC2KcJgcXnICJGmLTkPcrVIpfEw

像往常一样,使用 https://jwt.io/,我可以检查它是否包含此权限,实际上它是:

{
  "sub": "yyy@gmail.com",
  "name": "Mario null Rossi",
  "user_profiles": [
    "CLIENT"
  ],
  "exp": 1640286931,
  "iat": 1640200531,
  "authorities": [
    "CLIENT"
  ]
}

所以现在我使用这个 newtoken 来调用我的目标 API (/api/users/email/) 但是 API 无法通过它访问用户:

如您所见,使用此令牌,API 访问似乎被禁止。

为什么如果在我的配置中我指定 USER_MATCHER 列表中定义的 API(所以前一个目标 API)应该拥有包含 CLIENT 权限的令牌的用户也可以访问吗?

怎么了?或者我在权限定义逻辑中缺少什么?

听起来您希望 /api/users/email/ 端点可供客户端和管理员访问

而不是.antMatchers(ADMIN_MATCHER).hasAnyAuthority("ADMIN")

尝试.antMatchers(ADMIN_MATCHER).hasAnyAuthority("ADMIN", "CLIENT)