Spring Security 如何正确配置两个安全配置和两个过滤器在Spring Boot ?

8wigbo56  于 2022-11-24  发布在  Spring
关注(0)|答案(2)|浏览(249)

我已经在我的spring Boot 微服务项目中实现了安全性,要求有两种类型的配置,一种用于用户请求(来自Angular ),另一种来自其他服务。设计是使用JWT标记用于用户请求,API密钥用于系统调用。
以下是配置文件(一个文件),但我也尝试将其拆分为两个文件,但没有任何影响:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Configuration  
    @Order(1)
    public static class APISecurityConfig extends WebSecurityConfigurerAdapter {
        
        @Value("${my.api.key.header}") 
        private String principalRequestHeader;
        @Value("${my.api.key.token}") 
        private String principalRequestValue;
        
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            httpSecurity
                .cors().disable().csrf().disable();         
            httpSecurity
            .antMatcher("/api/users/**")
            .authorizeRequests() //
                .anyRequest().authenticated() 
                .and()
            .addFilterBefore(new APIKeyAuthFilter(principalRequestHeader, principalRequestValue), UsernamePasswordAuthenticationFilter.class);                                                                   
        }
              
        
    }
    
    @Order(2)
    @Configuration
    public static class MySecurityConfiguration extends WebSecurityConfigurerAdapter {
         
        @Autowired
        UserDetailsService userDetailsService;
            
        @Bean
        public AuthTokenFilter authenticationJwtTokenFilter() {
            return new AuthTokenFilter();
        }
        
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {      
            auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
        }
        
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
        
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
            
        @Override
          public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/api/users/**");
          }
        
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {          
            httpSecurity
                .cors().disable().csrf().disable();         
            httpSecurity
             .authorizeRequests()
             .antMatchers("/users/UserEmailExist", "/users/User/Add", "/users/Authenticate",
                     "/users/User/ChangePassword")
             .permitAll() 
             .and()                                     
             .authorizeRequests()            
             .antMatchers("/users/**").hasAnyRole(ROLE_ADMIN_USER, ROLE_MANAGER_USER)   
             .anyRequest().authenticated()                        
             .and()
             .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);      
        }
        
    }
    
}

每个配置都有一个附加的过滤器,这里是api过滤器:

public class APIKeyAuthFilter extends GenericFilterBean  {
    
    private String principalRequestHeader;  
    private String principalRequestValue;
        
    public APIKeyAuthFilter(String principalRequestHeader, String principalRequestValue) {
        super();
        this.principalRequestHeader = principalRequestHeader;
        this.principalRequestValue = principalRequestValue;
    }
            
     @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
         
            if(request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
                String apiKey = getApiKey((HttpServletRequest) request);
                if(apiKey != null) {
                    if(apiKey.equals(principalRequestValue)) {
                        ApiKeyAuthenticationToken apiToken = new ApiKeyAuthenticationToken(apiKey, AuthorityUtils.NO_AUTHORITIES);
                        SecurityContextHolder.getContext().setAuthentication(apiToken);
                    } else {
                        HttpServletResponse httpResponse = (HttpServletResponse) response;
                        httpResponse.setStatus(401);
                        httpResponse.getWriter().write("Invalid API Key");
                        return;
                    }
                }
            }
            
            chain.doFilter(request, response);
            
        }

}

下面是jwt(普通用户)的过滤器:

public class AuthTokenFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private MyUserDetailsService userDetailsService;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            String jwt = parseJwt(request);
            if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
                String username = jwtUtils.getUserNameFromJwtToken(jwt);

                MSUserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception e) {
            logger.error("Cannot set user authentication: {}", e);
        }

        filterChain.doFilter(request, response);

    }

}

我创建了两个不同的控制器,一个带有前缀/api/users,另一个带有前缀/users。
1.用户从Angular登录,获取jwt令牌并处理最终进入Jwt过滤器的请求,此场景看起来很好,没有任何问题,因为只要用户通过身份验证,就可以处理请求。
1.微服务将带有api-key的请求发送到带有/api/users前缀的url,该请求在正常用户结束的同一过滤器上结束,这是不正确的,如果没有JWT标记,他实际上能够继续到控制器并处理请求,而无需转到正确的过滤器。
我唯一的解决方案是只有一个过滤器,并处理api-key和jwt的头部,但它似乎不正确。我在网上看了看,并试图找出我做错了什么,但到目前为止没有线索。

gg0vcinb

gg0vcinb1#

关于这个问题的更新,所以我希望它会有助于社会。
首先,我删除了下面的代码,这主要是解决问题:

//      @Override
//        public void configure(WebSecurity web) throws Exception {
//          web.ignoring().antMatchers("/api/users/**");
//        }

该解决方案作为一个整体工作的方式是,第一个配置@Order(1)您定义了.antMatcher,这意味着该配置只对与前缀匹配的URL有效。场景1.来自Angular的用户只使用JWT过滤器。场景2. API用户将首先使用API过滤器!但是一旦完成(成功验证后)它仍然继续JWT过滤器,但因为它没有JWT过滤器,所以不执行任何操作。
我想避免其他过滤器的情况下,API调用,但解决方案的工作,问题解决了。我必须说,安全在Spring Boot 是最复杂的,我遇到了迄今为止从其他功能。

xkftehaa

xkftehaa2#

因为AuthTokenFilter是用@Bean示例化的,这导致过滤器被添加到ApplicationFilterChain,所以在处理完APIKeyAuthFilter之后,它也可以进入AuthTokenFilter

相关问题