为啥对于拥有包含“正确”权限的 JWT 令牌的用户禁止使用此 API?

Posted

技术标签:

【中文标题】为啥对于拥有包含“正确”权限的 JWT 令牌的用户禁止使用此 API?【英文标题】:Why this API is forbidden for an user having a JWT token containing the "correct" authority?为什么对于拥有包含“正确”权限的 JWT 令牌的用户禁止使用此 API? 【发布时间】:2022-01-23 23:29:12 【问题描述】:

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

基本上我有一个包含我的 Spring Security 配置的 SecurityConfiguration 类,正如您所见,它旨在处理 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 权限相关,这是目前可以执行的最简单的操作类型...请不要付费太在意权威的名字了……这些主要是一些例子,然后我会更好地定义我的权威列表)。

这样做,我希望这个 /api/users/email/ API 必须为具有 ADMIN 权限的用户和用户启用拥有CLIENT权限。

但是好像不是真的,做个例子。我为具有 ADMIN 权限的用户生成令牌,如下所示:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ4eHhAZ21haWwuY29tIiwibmFtZSI6IlJlbmF0byBudWxsIEdpYWxsaSIsInVzZXJfcHJvZmlsZXMiOlsiQURNSU4iXSwiZXhwIjoxNjQwMjg2NTY5LCJpYXQiOjE2NDAyMDAxNjksImF1dGhvcml0aWVzIjpbIkFETUlOIl19.WQGYKbo_ihrV2Nu1RlxrCweBpgU-Y-dNh9L6R9vrj3vhTyvbPlsyzWNPe8ljtP6WZ8_Vvv8FUDJIa6y5BLS1SA

使用https://jwt.io/网站可以看到这个token拥有ADMIN权限:


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

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

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

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 的令牌的用户访问强>权威?

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

【问题讨论】:

我建议您丢弃自定义令牌过滤器并使用自 2018 年以来 Spring Security 具有的内置 jwt 功能而不是 docs.spring.io/spring-security/reference/servlet/oauth2/…,因为编写自定义安全性是不好的做法,为什么要导入安全性框架如果你不打算使用它 【参考方案1】:

听起来您希望 CLIENT 和 ADMIN 都可以访问 /api/users/email/ 端点

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

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

【讨论】:

以上是关于为啥对于拥有包含“正确”权限的 JWT 令牌的用户禁止使用此 API?的主要内容,如果未能解决你的问题,请参考以下文章

用户权限更改后 JWT 刷新

如何在 JWT 令牌中包含声明?

如何使用 JWT 处理更改的权限

在 JWT 令牌中存储用户权限的最佳做法是啥?

如何使用 passport-jwt 正确检查令牌有效负载?

具有用户权限的基于 JWT 令牌的授权 Asp.net core 2.0