代码之家  ›  专栏  ›  技术社区  ›  AndreaNobili

这个Spring安全配置出了什么问题?如何使具有不同权限的用户可以访问端点?

  •  0
  • AndreaNobili  · 技术社区  · 4 年前

    我正在使用Spring Security和JWT令牌开发一个Spring引导程序来保护我的API,我有以下iusse。

    我有一个API处理端点,如下所示:http://localhost:8019/api/admin/user/54/wallet

    我有两种用户类型:

    • 管理员用户: 管理 在JWT标记中定义的autority。
    • 客户端用户: 客户 在JWT标记中定义的autority。

    之前的API必须能被两种用户类型访问(我知道 /管理员/ URI中的部分不是最好的。。。它将在不久的将来进行重构)。

    然后我让这门课扩展弹簧靴 Web安全配置适配器 类并实现我的安全配置:

    @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[] CLIENT_MATCHER = { 
    
                                                            "/api/users/email/*",
                                                            //"/api/admin/**",
                                                            "/api/admin/user/{userID}/wallet"
                                                        };
        
        private static final String[] ADMIN_MATCHER = { 
                                                            "/api/users/email/**",
                                                            "/api/admin/users",
                                                            "/api/admin/user/{userID}/wallet",
                                                            "/api/admin/**"
                                                            
                                                       };
        
        @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(CLIENT_MATCHER).hasAnyAuthority("CLIENT")
                       .antMatchers(ADMIN_MATCHER).hasAnyAuthority("ADMIN")
                       //.antMatchers(CLIENT_MATCHER).hasAnyAuthority("CLIENT")
                       .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, "/**");
            web.ignoring().antMatchers("/swagger-ui/**",
                                        "/webjars/**",
                                        "/v2/**",
                                        "/swagger-resources/**",
                                        "/swagger-ui.html");
        }
    
        @Bean
        public BCryptPasswordEncoder passwordEncoder()
        {
            return new BCryptPasswordEncoder();
        };
    
    
    }
    

    问题是,如果我尝试调用之前的API,正如前面的配置中定义的那样(http://localhost:8019/api/admin/user/54/wallet)与具有 客户 autority我正在获取此错误消息:

    {
        "timestamp": "2022-02-13T20:22:10.418+00:00",
        "status": 403,
        "error": "Forbidden",
        "message": "Forbidden",
        "path": "/api/admin/user/54/wallet"
    }
    

    反之,如果我试图调用前一个API,并传递一个拥有 阿姆丁 授权我获得预期的API输出。

    这对我来说很奇怪,因为 客户匹配器 我定义了这个规则:

    "/api/admin/user/{userID}/wallet"
    

    最奇怪的是如果我从 管理员匹配器 :

    "/api/admin/user/{userID}/wallet",
    

    变成了这样:

    private static final String[] ADMIN_MATCHER = { 
                                                        "/api/users/email/**",
                                                        "/api/admin/users",
                                                        //"/api/admin/user/{userID}/wallet",
                                                        "/api/admin/**"
                                                        }
    

    现在的行为完全相反:使用拥有 客户 授权我检索预期的输出,但现在使用拥有 管理 authoruty I获取此错误消息:

    { “时间戳”:“2022-02-13T20:27:47.793+00:00”, “状态”:403, “错误”:“禁止”, “信息”:“禁止”, “路径”:“/api/admin/user/54/wallet” }

    这对我来说很奇怪因为 管理员匹配器 继续这条规则:

    "/api/admin/**"
    

    这应该意味着: 访问所有API,其enptoint以“/API/admin/”开头,后跟任何其他内容 .

    我的Spring配置有什么问题?我错过了什么?我如何修复它,使这个API可以使用JWT令牌访问 管理 客户 权威

    0 回复  |  直到 4 年前
        1
  •  0
  •   indybee    4 年前

    而不是

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

    尝试

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

    从客户机匹配器中删除这个

    "/api/admin/user/{userID}/wallet"
    
        2
  •  0
  •   M. Deinum    4 年前

    问题在于你制定规则的方式。匹配的规则就是所使用的规则,因此如果请求进入并找到 "/api/user/email/**" 它将找到第一个匹配的,一个供用户使用的。

    你应该写的是 .antMatchers("/api/user/email/**").hasAnyAuthority("USER", "CLIENT", "ADMIN") 允许所有人进入。这是你的安全规则,它不会检查所有规则,它会首先检测匹配的规则(这也意味着排序对你的规则非常重要!)。

    顺便说一句,这也适用于你的所有其他规则。