Spring Auth + JWT-ответ 200, но тело и заголовки отсутствуют - PullRequest
0 голосов
/ 22 декабря 2018

Мне тяжело с Spring Security.В основном у меня есть /auth/signup и /auth/login для создания учетной записи и получения токена jwt соответственно.

Оба прекрасно работают локально через Insomnia / POSTman , но при отправке запроса на /auth/login изинтерфейс React.js с fetch() либо возвращает ошибку запроса предпечатной проверки CORS, либо дает ответ 200 «непрозрачный» или «cors» OK с без тела .Последний бесполезен, так как мне нужен токен.Что раздражает, так это то, что он работает на бессоннице.

Вот как выглядит ответ, когда он запускается через fetch() из javascript локально.Там нет тела и нет заголовков.Где, как и в случае с бессонницей или POSTman, есть заголовки и тело.

enter image description here

За последние несколько недель я пробовал несколько разных вещей, включая:

  • Добавление пользовательского addCorsMappings() вызова в значении WebMvcConfigurer
  • CORS фильтр @Component, который переопределяет doFiler() и имеет набор setHeader() вызовов.
  • Класс конфигурации CORS с corsConfigurer() как @Bean
  • Другой Bean внутри пользовательского WebSecurityConfigurerAdapter, возвращающий CorsConfigurationSource

ПравдаЯ не знаю, какой из них самый современный, правильный для использования.Я прочитал так много вещей (в основном статьи и руководства по переполнению стека), которые не помогли.Это так неправильно, просто вставлять фрагменты без полного понимания того, сработает ли это или как.Если кто-нибудь может описать правильный способ настройки CORS для работы с fetch() вызовом из React, а не только с POSTman / Insomia - я был бы очень признателен.

Я включу столько источников, сколько смогу, но исключит такие вещи, как пользовательские классы здесь.Насколько я понимаю, они довольно стандартны, по большей части.

WebMvcConfig.java:

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {

    private final long MAX_AGE_SECS = 360;

    @Override
    public void addCorsMappings(CorsRegistry registry){
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedHeaders("Content-Type", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Authorization", "X-Requested-With", "requestId", "Correlation-Id")
                .exposedHeaders("Content-Type", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Authorization", "X-Requested-With", "requestId", "Correlation-Id")
                .allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE")
                .maxAge(MAX_AGE_SECS);
    }
}

SimpleCORSFilter.java:

@Component
public class SimpleCORSFilter implements Filter {


    public SimpleCORSFilter() {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");


        response.setHeader("Access-Control-Allow-Headers", "origin, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Authorization, requestId, Correlation-Id, Content-Type, Accept, X-Requested-With, remember-me");

        chain.doFilter(req, res);
    }

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void destroy() {
    }

}

CorsConfig.java:

@Configuration
public class CorsConfig {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedMethods("OPTIONS", "GET", "POST", "PUT", "DELETE").allowedOrigins("*")
                        .allowedHeaders("*");
            }
        };
    }
}

Теперь вот класс SecurityConfig.java, использующий WebSecurityConfigurerAdapter.Обратите внимание на методы configure(HttpSecurity http) и CorsConfigurationSource corsConfigurationSource().

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        securedEnabled = true,
        jsr250Enabled = true,
        prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    CustomUserDetailsService customUserDetailsService;

    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;

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


    @Override
    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.
                userDetailsService(customUserDetailsService)
                .passwordEncoder(passwordEncoder());

    }

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


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

    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                .and()
                .csrf()
                .disable()
                .authorizeRequests()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(unauthorizedHandler)
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/",
                        "/favicon.ico",
                        "/**/*.png",
                        "/**/*.gif",
                        "/**/*.svg",
                        "/**/*.jpg",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js")
                .permitAll()
                .antMatchers("/auth/**")
                .permitAll()
                .antMatchers("/user/checkUsernameAvailability", "/user/checkEmailAvailability")
                .permitAll()
                .antMatchers(HttpMethod.GET, "/polls/**", "/users/**")
                .permitAll()
                .anyRequest()
                .authenticated();

        // Add our custom JWT security filter
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token", "Content-Type", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Authorization", "X-Requested-With", "requestId", "Correlation-Id"));
        configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }



}

У меня есть эти два маршрута в контроллере /auth:

   @CrossOrigin
    @PostMapping(path = "/login", produces = { "application/json" }, consumes = { "application/json" })
    public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest){
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(loginRequest.getUsernameOrEmail(),
                        loginRequest.getPassword())
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);

        String jwt = tokenProvider.generateToken(authentication);
        return ResponseEntity.ok().body(new JwtAuthenticationResponse(jwt));
    }

    @CrossOrigin
    @PostMapping(path = "/signup", produces = { "application/json" }, consumes = { "application/json" })
    public ResponseEntity<?> registerUser(@Valid @RequestBody SignUpRequest signUpRequest){

        if (userClient.existsByUsername(signUpRequest.getUsername())) {
            return new ResponseEntity(new ApiResponse(false, "Username already being used!"),HttpStatus.BAD_REQUEST);
        }
        AppUser user = new AppUser(signUpRequest.getUsername(), signUpRequest.getPassword(), "ADMIN");
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        AppUser result = userClient.save(user);


        URI location = ServletUriComponentsBuilder.fromCurrentContextPath().path("/users/{useranme}").buildAndExpand(result.getUsername()).toUri();
        return ResponseEntity.created(location).body(new ApiResponse(true, "Success: User registered"));

    }

Наконец вызов вызова javascript:

fetch(`${config.apiUrl}/auth/login`, {
            method: 'POST',
            mode: 'cors',
            headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', 'accept':'*/*' },
            body: JSON.stringify({ "usernameOrEmail": username, "password": password })
        })

Ответы [ 2 ]

0 голосов
/ 28 декабря 2018

Я использую CrosFilter с интерфейсом React.После применения CrosFilter у меня не возникло никаких проблем.Попробуйте использовать CrosFilter.Найдите приведенный ниже образец.

@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowCredentials(true);
    configuration.addAllowedOrigin("*");
    configuration.addAllowedHeader("*");
    configuration.addAllowedMethod("OPTIONS");
    configuration.addAllowedMethod("HEAD");
    configuration.addAllowedMethod("GET");
    configuration.addAllowedMethod("PUT");
    configuration.addAllowedMethod("POST");
    configuration.addAllowedMethod("DELETE");
    configuration.addAllowedMethod("PATCH");
    source.registerCorsConfiguration("/**", configuration);
    return new CorsFilter(source);
}
0 голосов
/ 22 декабря 2018

Я думаю, вы пропустили аннотатор сканирования компонентов.Следующий код работает для меня.

@Configuration
@EnableWebMvc
@ComponentScan
public class MainAppConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("*");
    }
}
...