使用登录过滤器而非控制器时处理选项和CORS [英] Handling OPTIONS and CORS when using a sign in filter instead of controller

查看:52
本文介绍了使用登录过滤器而非控制器时处理选项和CORS的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个AbstractAuthenticationProcessingFilter,用于处理路径/sign-in上的POST请求.由于没有匹配的路径,因此CORS预检请求将返回404.这对我来说很有意义.

I've got an AbstractAuthenticationProcessingFilter that I'm using to handle POST requests at path /sign-in. CORS preflight requests are coming back 404 because there is no path that matches. This makes sense to me.

我想知道的是,是否有一种方法可以通知Spring有一个处理POST的过滤器(而不是控制器),以便Spring可以像在控制器中使用一样分派OPTIONS.处理POST.用一个PostMapping编写一个控制器是不好的做法吗?我不确定如何处理,因为从技术上讲,过滤器会处理POST.

What I would like to know is if there is a way to inform Spring that there is a filter handling the POST (rather than a controller), so that Spring can dispatch the OPTIONS in the same way it would if a controller were handling the POST. Would it be bad practice to write a controller with one PostMapping? I'm not sure how that would behave since technically the filter handles the POST.

感谢您的帮助!

更新

这是我的设置.我最初是通过手机发布的,因此当时无法添加这些详细信息.见下文.重申一下,没有/sign-in的控制器. POST由JwtSignInFilter处理.

Here's my setup. I originally posted from my phone so wasn't able to add these details then. See below. To reiterate, there is no controller for /sign-in. The POST is handled by the JwtSignInFilter.

CORS配置

@EnableWebMvc
@Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedOrigins("*")  // TODO: Lock this down before deploying
            .allowedHeaders("*")
            .allowedMethods(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())
            .allowCredentials(true);
    }
}

安全配置

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public JwtSignInFilter signInFilter() throws Exception {
        return new JwtSignInFilter(
            new AntPathRequestMatcher("/sign-in", HttpMethod.POST.name()),
            authenticationManager()
        );
    }

    @Bean
    public JwtAuthenticationFilter authFilter() {
        return new JwtAuthenticationFilter();
    }

    @Autowired
    private UserDetailsService userDetailsService;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            .antMatchers(HttpMethod.POST, "/sign-in").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(
                signInFilter(),
                UsernamePasswordAuthenticationFilter.class
            )
            .addFilterBefore(
                authFilter(),
                UsernamePasswordAuthenticationFilter.class
            );
    }
}

登录过滤器

public class JwtSignInFilter extends AbstractAuthenticationProcessingFilter {

    @Autowired
    private TokenAuthenticationService tokenAuthService;

    public JwtSignInFilter(RequestMatcher requestMatcher, AuthenticationManager authManager) {
        super(requestMatcher);
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException, IOException, ServletException {
        SignInRequest creds = new ObjectMapper().readValue(
            req.getInputStream(),
            SignInRequest.class
        );

        return getAuthenticationManager().authenticate(
            new UsernamePasswordAuthenticationToken(
                creds.getEmail(),
                creds.getPassword(),
                emptyList()
            )
        );
    }

    @Override
    protected void successfulAuthentication(
        HttpServletRequest req,
        HttpServletResponse res, FilterChain chain,
        Authentication auth) throws IOException, ServletException {
        tokenAuthService.addAuthentication(res, auth.getName());
    }
}

身份验证过滤器

public class JwtAuthenticationFilter extends GenericFilterBean {

    @Autowired
    private TokenAuthenticationService tokenAuthService;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        Authentication authentication = tokenAuthService.getAuthentication((HttpServletRequest)request);
        SecurityContextHolder
            .getContext()
            .setAuthentication(authentication);
        filterChain.doFilter(request, response);
    }
}

推荐答案

好的,终于找到了解决方法.经过数小时的修改和搜索,我发现我需要使用基于过滤器的CORS配置,然后只需返回200 OK,即可处理登录过滤器中的CORS预检(OPTIONS请求).然后,CORS过滤器将添加适当的标头.

Alright, finally found out how to fix this. After hours of tinkering and searching, I found that I needed to use a filter-based CORS configuration and then handle CORS preflights (OPTIONS requests) in the sign-in filter by simply returning 200 OK. The CORS filter will then add appropriate headers.

下面的更新配置(请注意,不再需要我的CorsConfig,因为我们在SecurityConfig中有一个CORS过滤器,并且JwtAuthenticationFilter与以前相同).

Updated configuration below (note that my CorsConfig is no longer needed, since we have a CORS filter in SecurityConfig, and JwtAuthenticationFilter is the same as before).

安全配置

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");  // TODO: lock down before deploying
        config.addAllowedHeader("*");
        config.addExposedHeader(HttpHeaders.AUTHORIZATION);
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }

    @Bean
    public JwtSignInFilter signInFilter() throws Exception {
        return new JwtSignInFilter(
            new AntPathRequestMatcher("/sign-in"),
            authenticationManager()
        );
    }

    @Bean
    public JwtAuthenticationFilter authFilter() {
        return new JwtAuthenticationFilter();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .cors()
            .and()
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/sign-in").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(
                signInFilter(),
                UsernamePasswordAuthenticationFilter.class
            )
            .addFilterBefore(
                authFilter(),
                UsernamePasswordAuthenticationFilter.class
            );
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }

    @Autowired
    private UserDetailsService userDetailsService;

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

登录过滤器

public class JwtSignInFilter extends AbstractAuthenticationProcessingFilter {

    @Autowired
    private TokenAuthenticationService tokenAuthService;

    public JwtSignInFilter(RequestMatcher requestMatcher, AuthenticationManager authManager) {
        super(requestMatcher);
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException, IOException, ServletException {
        if (CorsUtils.isPreFlightRequest(req)) {
            res.setStatus(HttpServletResponse.SC_OK);
            return null;
        }

        if (!req.getMethod().equals(HttpMethod.POST.name())) {
            res.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return null;
        }

        SignInRequest creds = new ObjectMapper().readValue(
            req.getInputStream(),
            SignInRequest.class
        );

        return getAuthenticationManager().authenticate(
            new UsernamePasswordAuthenticationToken(
                creds.getEmail(),
                creds.getPassword(),
                emptyList()
            )
        );
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException, ServletException {
        tokenAuthService.addAuthentication(res, auth.getName());
    }
}

这篇关于使用登录过滤器而非控制器时处理选项和CORS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆