首页 > 解决方案 > Spring Boot 和 Spring Security 过滤器未过滤正确的请求

问题描述

我有一个弹簧靴和弹簧安全服务。我扩展了 WebSecurityConfigurerAdapter 类并重写了配置方法。但不知何故,它没有过滤正确的请求。

我的网址类似于 -

localhost:8080/album/private/v1/getAlbumsByVendorId?vendorId=1
 localhost:8080/vendor/private/v1/getVendor?vendorId=1

而且我有一些我不想验证的 URL。就像下面的 url。

localhost:8080/category/v1/getCategory

如果 URL 包含私有,则只想进行身份验证。但似乎我的过滤器正在为所有请求调用。有什么问题吗.antMatchers("/**/private/**")

注意 - 我现在没有任何上下文路径。添加了类。控制器只是一个虚拟测试控制器。

@Configuration
@EnableWebSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private UserDetailsService jwtUserDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        // configure AuthenticationManager so that it knows from where to load
        // user for matching credentials
        // Use BCryptPasswordEncoder
        auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
    }

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

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
        .csrf().disable()
        .cors().disable()
        .authorizeRequests()
        .antMatchers("/authenticate").permitAll()
        .antMatchers("/**/private/**").authenticated()
        .anyRequest().authenticated()
        .and()
        .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
        .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint);
    }
}


    @Component
    public class JWTAuthenticationFilter extends OncePerRequestFilter {

        @Autowired
        private JwtUserDetailsService jwtUserDetailsService;

        @Autowired
        private JwtTokenUtil jwtTokenUtil;

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                throws ServletException, IOException {

            final String requestTokenHeader = request.getHeader("Authorization");

            String username = null;
            String jwtToken = null;
            // JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token
            if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
                jwtToken = requestTokenHeader.substring(7);
                try {
                    username = jwtTokenUtil.getUsernameFromToken(jwtToken);
                } catch (IllegalArgumentException e) {
                    System.out.println("Unable to get JWT Token");
                } catch (ExpiredJwtException e) {
                    System.out.println("JWT Token has expired");
                }
            } else {
                logger.warn("JWT Token does not begin with Bearer String");
            }

            //Once we get the token validate it.
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

                UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);

                // if token is valid configure Spring Security to manually set authentication
                if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {

                    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                            userDetails, null, userDetails.getAuthorities());
                    usernamePasswordAuthenticationToken
                            .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    // After setting the Authentication in the context, we specify
                    // that the current user is authenticated. So it passes the Spring Security Configurations successfully.
                    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                }
            }
            chain.doFilter(request, response);
        }

    }



@RestController
@CrossOrigin()
public class HelloWorldController {

    @RequestMapping({ "/hello" })
    public String hello() {
        return "Hello World";
    }

    @RequestMapping({ "/private/test" })
    public String hello2() {
        return "Hello World-test";
    }

    @RequestMapping({ "/v1/private/test" })
    public String hello3() {
        return "Hello World-test-v1";
    }

    @RequestMapping({ "/v1/public/test" })
    public String hello4() {
        return "Hello World-test-v1-public";
    }

}

标签: springspring-bootspring-security

解决方案


默认情况下,当 Spring Security 在类路径上时,Spring Boot 将保护所有端点。我们需要明确地为所有其他端点添加一个排除项,以便在没有身份验证的情况下被允许。考虑更改 is .anyRequest().permitAll(),这意味着每个/**/private/**人都可以访问除此之外的每个请求。换句话说,过滤器将仅适用于/**/private/**

Git 链接

方法1(干净的方式)

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .csrf().disable()
        .cors().disable()
        .authorizeRequests()
            .antMatchers("/authenticate").permitAll()
            .antMatchers("/**/private/**").authenticated()
            .anyRequest().permitAll()
            .and()
        .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
        .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint);
    }

方法 2:仅在 Request 来自/private/ 时检查令牌(不是理想的方式)

JwtAuthenticationEntryPoint.java

@Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException {

        System.out.println("Entry Request: "+request.getRequestURI());
        System.out.println("Entry Contain: "+request.getRequestURI().contains("private"));
        if(request.getRequestURI().contains("private")==true) 
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");

    } 

JwtRequestFilter.java

@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        final String requestTokenHeader = request.getHeader("Authorization");
        System.out.println("JWT Request: "+request.getRequestURI());
        System.out.println("JWT Contain: "+request.getRequestURI().contains("private"));
        String username = null;
        String jwtToken = null;
        //Remove comment for second approach
        if(request.getRequestURI().contains("private")==false)
        {
            System.out.println("Do Noting, Permit It");
            chain.doFilter(request, response);
        }
        else if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ") ) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);
            } catch (IllegalArgumentException e) {
                System.out.println("Unable to get JWT Token");
            } catch (ExpiredJwtException e) {
                System.out.println("JWT Token has expired");
            }
        } else {
            logger.warn("JWT Token does not begin with Bearer String");
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);

            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {

                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        chain.doFilter(request, response);
    }

http://localhost:8080/v1/private/test **401**

http://localhost:8080/v1/private/test

http://localhost:8080/v1/public/test **200**

http://localhost:8080/v1/public/test


推荐阅读