programing

Spring Security 필터를 보안 엔드포인트에만 적용하는 방법은 무엇입니까?

muds 2023. 8. 24. 22:31
반응형

Spring Security 필터를 보안 엔드포인트에만 적용하는 방법은 무엇입니까?

Spring Security 구성은 다음과 같습니다.

httpSecurity
        .csrf().disable()
        .exceptionHandling()
            .authenticationEntryPoint(unauthorizedHandler)
            .and()
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
        .authorizeRequests()
            .antMatchers("/api/**").fullyAuthenticated()
            .and()
        .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);

authenticationTokenFilterBean()일치하지 않는 엔드포인트에도 적용됩니다./api/**표현.다음 구성 코드도 추가해 보았습니다.

@Override
public void configure(WebSecurity webSecurity) {
    webSecurity.ignoring().antMatchers("/some_endpoint");
}

하지만 이것은 여전히 제 문제를 해결하지 못했습니다.Spring Security에서 보안된 URI 식과 일치하는 엔드포인트에만 필터를 적용하도록 지시하려면 어떻게 해야 합니까?

동일한 요구 사항을 가진 애플리케이션이 있으며 이를 해결하기 위해 기본적으로 스프링 보안을 주어진 개미 매치 패턴으로 제한했습니다(사용).antMatcher) 다음과 같습니다.

http
    .antMatcher("/api/**")
    .authorizeRequests() //
        .anyRequest().authenticated() //
        .and()
    .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);

다음과 같이 읽을 수 있습니다.http개미 패턴과 일치하는 요청에 대해서만 이러한 구성을 호출합니다./api/**권한을 부여any request로.authenticated사용자들and add filter authenticationTokenFilterBean() before UsernamePasswordAuthenticationFilter다른 모든 요청에 대해 이 구성은 적용되지 않습니다.

GenericFilterBean에는 다음과 같은 방법이 있습니다.

/**
     * Can be overridden in subclasses for custom filtering control,
     * returning {@code true} to avoid filtering of the given request.
     * <p>The default implementation always returns {@code false}.
     * @param request current HTTP request
     * @return whether the given request should <i>not</i> be filtered
     * @throws ServletException in case of errors
     */
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        return false;
    }

그래서 당신의 필터에서 그것은 확장됩니다.GenericFilterBean이 메서드를 재정의하고 원하는 경로에서만 필터를 실행하는 논리를 구현할 수 있습니다.

제 요구 사항은 엔드포인트 일치 /api/auth/**를 제외하는 것이었는데, 이를 위해 WebSecurityConfig 스프링 구성 요소를 다음과 같이 구성했습니다.

/**
 * The purpose of this method is to exclude the URL's specific to Login, Swagger UI and static files.
 * Any URL that should be excluded from the Spring security chain should be added to the ignore list in this
 * method only
 */
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/api/auth/**","/v2/api-docs", 
            "/configuration/ui", 
            "/swagger-resources", 
            "/configuration/security",
            "/swagger-ui.html", 
            "/webjars/**",
            "/favicon.ico",
            "/**/*.png",
            "/**/*.gif",
            "/**/*.svg",
            "/**/*.jpg",
            "/**/*.html",
            "/**/*.css",
            "/**/*.js");
}


   /**
     * The purpose of this method is to define the HTTP configuration that defines how an HTTP request is 
     * going to be treated by the Spring Security chain. All the request URL's (excluding the URL's added
     * in WebSecurity configuration ignore list) matching this configuration have to pass through the
     * custom Spring security filter defined in this method
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
        .cors().disable()
        .authorizeRequests()
        .anyRequest()
        .authenticated()
        .and()
        .exceptionHandling()
        .authenticationEntryPoint(unauthorizedHandler)
        .and()
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
    }

/**
 * The purpose of this method is to create a new instance of JWTAuthenticationFilter
 * and return the same from the method body. It must be ensured that this filter should
 * not be configured as a Spring bean or registered into the Spring Application context
 * failing which the below filter shall be registered as a default web filter, and thus
 * all the URL's even the excluded ones shall be intercepted by the below filter
 */
public JWTAuthenticationFilter authenticationTokenFilterBean() {
    return new JWTAuthenticationFilter();
}

최근에 Spring Security 6.0.0을 사용하는 Spring Boot 3.0.0으로 업데이트했으며 모든 요청에 필터가 적용되었을 때 유사한 문제가 발생했습니다.authorizeHttpRequests()특정 경로가 정의된 상태에서 사용되었습니다.

알고 보니, 만약 당신이 원한다면.HttpSecurity특정 경로에 대해 구성하려면 사용해야 합니다.securityMatcher()처음에

그래서 다음과 같은 것이 될 것입니다.

private SecurityFilterChain configureFilterChain(HttpSecurity http, String pattern, String... roles) throws Exception {
    return http
               .securityMatcher(pattern)
               .authorizeHttpRequests(auth -> auth.requestMatchers(AntPathRequestMatcher.antMatcher(pattern)).hasAnyRole(roles))
               .addFilterBefore(new TokenFilter(), UsernamePasswordAuthenticationFilter.class)
               .sessionManagement()
                   .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                   .and()
               .exceptionHandling()
                   .authenticationEntryPoint(new AuthenticationEntryPointImpl())
                   .accessDeniedHandler(new AccessDeniedHandlerImpl())
                   .and()
               .csrf().disable()
               .build();
}

그래서 이 경우에는TokenFilter다음이 포함된 요청에만 적용됩니다.pattern.

를 사용하는 경우

.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

생성자에서 적용할 특정 경로를 정의할 수 있습니다.

public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super("/api/**");
        this.setAuthenticationManager(authenticationManager);
    }

    @Override
    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
        return super.requiresAuthentication(request, response);
    }

requiresAuthentication메소드를 사용하여 해당 엔드포인트에 인증이 필요한지 여부를 알 수 있습니다.

해결할 방법을 찾은 것 같습니다.있습니다JwtTokenAuthenticationProcessingFilter그것은AbstractAuthenticationProcessingFilter나는 헤드에 토큰이 있으면 인증 요청을 하고, 실패하면 차단하지 않기를 원합니다.필요한 것은 doFilter를 다시 작성하고 인증 결과에 관계없이 를 호출하는 것입니다(실패한 인증 호출은 선택 사항).여기 제 코드의 일부가 있습니다.

public class JwtTokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

    private final TokenExtractor tokenExtractor;

    @Autowired
    public JwtTokenAuthenticationProcessingFilter(TokenExtractor tokenExtractor, RequestMatcher matcher) {
        super(matcher);
        this.tokenExtractor = tokenExtractor;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
            ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        if (!this.requiresAuthentication(request, response)) {
            chain.doFilter(request, response);
        } else {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Request is to process authentication");
            }

            boolean success = true;

            Authentication authResult = null;
            try {
                authResult = this.attemptAuthentication(request, response);
            } catch (InternalAuthenticationServiceException var8) {
                this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
                success = false;
            } catch (AuthenticationException var9) {
                success = false;
            }


            if (success && null != authResult) {
                this.successfulAuthentication(request, response, chain, authResult);
            }

            // Please ensure that chain.doFilter(request, response) is invoked upon successful authentication. You want
            // processing of the request to advance to the next filter, because very last one filter
            // FilterSecurityInterceptor#doFilter is responsible to actually invoke method in your controller that is
            // handling requested API resource.
            chain.doFilter(request, response);
        }
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        String tokenPayload = request.getHeader(WebSecurityConfig.AUTHENTICATION_HEADER_NAME);
        RawAccessJwtToken token = new RawAccessJwtToken(tokenExtractor.extract(tokenPayload));
        return getAuthenticationManager().authenticate(new JwtAuthenticationToken(token));
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(authResult);
        SecurityContextHolder.setContext(context);
    }
}

4월 22일 업데이트

필터를 등록하려면 WebSecurityConfig에 다음 코드를 추가하면 됩니다.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtAuthenticationProvider mJwtAuthenticationProvider;

    @Autowired
    public WebSecurityConfig(JwtAuthenticationProvider jwtAuthenticationProvider) {
        this.mJwtAuthenticationProvider = jwtAuthenticationProvider;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // When multiple authentication providers are defined, the providers will be queried in the order they’re
        // declared.
        auth.authenticationProvider(mJwtAuthenticationProvider);
    }
}

코드에서 필터 추가에 대한 중요한 부분만 공개했습니다.이 모든 구현은 이 사이트에서 영감을 받았습니다.작가 블라디미르 스탄코비치의 자세한 설명을 들어보세요.

언급URL : https://stackoverflow.com/questions/36795894/how-to-apply-spring-security-filter-only-on-secured-endpoints

반응형