简体   繁体   English

Header axios 中定义的内容类型不起作用

[英]Header Content-Type defined in axios not working

I'm having a little trouble to integrate my login frontend logics ( Vue3 + Axios ) to my Java API (with Spring Security ).我在将我的登录前端逻辑( Vue3 + Axios )集成到我的 Java API(带有Spring Security )时遇到了一些麻烦。

I'm getting the following error:我收到以下错误:

com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
 at [Source: (org.apache.catalina.connector.CoyoteInputStream); line: 1, column: 0]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) ~[jackson-databind-2.10.2.jar:2.10.2]
    at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4344) ~[jackson-databind-2.10.2.jar:2.10.2]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4189) ~[jackson-databind-2.10.2.jar:2.10.2]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3242) ~[jackson-databind-2.10.2.jar:2.10.2]
    at br.com.preferencialsaude.app.JWTLoginFilter.attemptAuthentication(JWTLoginFilter.java:35) ~[classes/:na]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.2.1.RELEASE.jar:5.2.1.RELEASE]

While debugging I've found out that no "Content-Type: application/json" header is present in my HttpServletRequest, but the problem is that I'm actually setting it in my axios client .调试时我发现我的 HttpServletRequest 中不存在“Content-Type: application/json”header,但问题是我实际上是在我的axios 客户端中设置它。

When I make the request from postman, it works fine, but when I try to make from my frontend app, I get the previous error.当我从 postman 发出请求时,它工作正常,但是当我尝试从我的前端应用程序发出请求时,我得到了之前的错误。

I thought it could be the browser, but it ends up in the same freaking error in any browser.我以为它可能是浏览器,但它最终在任何浏览器中都出现了同样的错误。 I've even tried to make the request with JS Fecth API, but the behavior remains the same.我什至尝试使用 JS Fecth API 发出请求,但行为保持不变。

The MimeHeaderFields when I make the request from browser:当我从浏览器发出请求时的 MimeHeaderFields:

host, connection, accept, access-control-request-method, access-control-request-headers,
origin,user-agent, sec-fetch-mode, sec-fetch-site, sec-fetch-dest, referer,
accept-encoding, accept-language

As you can see, there's no Content-Type , but it is present when I make request from postman.如您所见,没有Content-Type ,但当我从 postman 发出请求时它存在。

Here is my client-side code:这是我的客户端代码:

async handleSubmit () {
    const response = await axios.post('http://localhost:8080/ws/login',
      { username: this.login, password: this.senha }, { 
        headers: { 
          'Content-Type': 'application/json',
        }
      });

    console.log(response.data)
    localStorage.setItem('token', response.data.token);
}

And here is my backend Java filter:这是我的后端 Java 过滤器:

import java.io.IOException;
import java.util.Collections;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.fasterxml.jackson.databind.ObjectMapper;

import br.com.preferencialsaude.app.model.AccountCredentials;
import br.com.preferencialsaude.app.service.TokenAuthenticationService;

public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {

    protected JWTLoginFilter(String url, AuthenticationManager authManager) {
        super(new AntPathRequestMatcher(url));
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {

        //here is where I get the error, due to the lack of Content-Type header
        AccountCredentials credentials = new ObjectMapper().readValue(request.getInputStream(), AccountCredentials.class);

        UsernamePasswordAuthenticationToken userAuthData =
            new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword(), Collections.emptyList());

        return getAuthenticationManager().authenticate(userAuthData);
    }

And this is the result I get in postman:这是我在 postman 中得到的结果:

{"token":"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aGFseXMiLCJleHAiOjE2NDYzMzM2OTB9.5wYyYs6jwhIsxob99Ubx4b64jIIMjVKXf0bZ_ZS_zhkPtrK0KOgKxOgy0HgxlEdV_F5DTwOeDooqVRouEuAqEA"}

with status code 200状态200

Here is my WebSecurityConfig class:这是我的 WebSecurityConfig class:

package br.com.preferencialsaude.app;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import br.com.preferencialsaude.app.service.MyUserDetailsService;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
    
    private MyUserDetailsService userDetailsService;
    
    public WebSecurityConfig(MyUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
    
    public BCryptPasswordEncoder passwordEncoder() {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        return bCryptPasswordEncoder;
    }
    
    @Autowired
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
            .antMatchers("/", "/login").permitAll()
            .antMatchers(HttpMethod.POST, "/ws/login").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(new JWTLoginFilter("/ws/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
            .formLogin()
                .loginPage("/login")
                .failureUrl("/login?error=true")
                .defaultSuccessUrl("/gerenciador/home", true)
                .usernameParameter("usuario")
                .passwordParameter("senha")
                .and()
            .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/gerenciador/logout"))
                .logoutSuccessUrl("/gerenciador/login").and().exceptionHandling()
                .accessDeniedPage("/error");
    }
    
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
           "/resources/**", "/static/**", "/css/**", "/js/**", "/imagens/**",
            "/webfonts/**", "/jasper/**", "/contratos/**",
            "/v2/api-docs", "/swagger-resources/**", "/configuration/ui", "/swagger-ui.html", "/webjars/**",
            "/procedimento/**", "/procedimento/**/**", "/credenciamento/empresa/**", "/credenciamento/pessoa/**",
            "/credenciamento/farmacia/**", "/buscaCep", "/plano/**", "/contrato/empresa/**",
            "/contrato/pessoa-fisica/**", "/rede-credenciada/**", "/ws/rede-credenciada/**", "/ws/endereco/**",
            "/ws/procedimentos/**", "/ws/recuperar-senha/**", "/api/cupons-venda/**", "/atendimento/**",
            "/termos", "/convenio/**", "/ws/agendamento/**", "/ws/consulta/**", "/ws/notificacoes/**", "/ws/avaliacao/**",
            "/ws/imagens/**", "/actuator/**", "/api/enderecos/**", "/api/cadastros-clientes/**", "/api/cidades/**",
            "/api/planos/**", "/api/especialidades-saude/consultas/**", "/api/rede-atendimento/**"
        );
    }
}

And here is my JWTLoginFilter:这是我的 JWTLoginFilter:

package br.com.preferencialsaude.app;

import java.io.IOException;
import java.util.Collections;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.fasterxml.jackson.databind.ObjectMapper;

import br.com.preferencialsaude.app.model.AccountCredentials;
import br.com.preferencialsaude.app.service.TokenAuthenticationService;

public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {

    protected JWTLoginFilter(String url, AuthenticationManager authManager) {
        super(new AntPathRequestMatcher(url));
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {

        AccountCredentials credentials = new ObjectMapper().readValue(request.getInputStream(), AccountCredentials.class);

        UsernamePasswordAuthenticationToken userAuthData =
            new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword(), Collections.emptyList());

        return getAuthenticationManager().authenticate(userAuthData);
    }
    
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain filterChain, Authentication auth) throws IOException, ServletException {
        TokenAuthenticationService.addAuthentication(response, auth.getName());
    }
    
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
        SecurityContextHolder.clearContext();
        TokenAuthenticationService.failAuthentication(response);
    }
}

Have you ever faced a similar problem?你遇到过类似的问题吗? I appreciate any help!感谢您的帮助!

I figured it out how to resolve this by adding some CORS configuration in my WebSecurityConfig file.我想出了如何通过在我的 WebSecurityConfig 文件中添加一些 CORS 配置来解决这个问题。

The problem wasn't the Content-Type Header at all, but Spring Security that was blocking OPTIONS requests.问题根本不是 Content-Type Header,而是阻止 OPTIONS 请求的 Spring Security。

This is the piece of code I added to solve the issue这是我为解决问题而添加的代码

@Bean
public FilterRegistrationBean processCorsFilter() {     
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.setAllowedOrigins(Arrays.asList("http://localhost:8080", "http://localhost:8081",
                "https://site.preferencialsaude.com.br", "https://site-hmlg.preferencialsaude.com.br"));
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/ws/login", config);
    source.registerCorsConfiguration("/api/**", config);


    final FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return bean;
}

Found this code in a comment inside this CORS configuration tutorial: https://spring.io/blog/2015/06/08/cors-support-in-spring-framework在此 CORS 配置教程中的评论中找到此代码: https://spring.io/blog/2015/06/08/cors-support-in-spring-framework

If you want to accept requests from all origins, just replace the setAllowedOrigins() with addAllowedOrigin("*") , since the way I configured it, as in the code above, I cannot login from postman, or any other origins except the ones I defined.如果您想接受来自所有来源的请求,只需将setAllowedOrigins() with addAllowedOrigin("*") ,因为我配置它的方式,如上面的代码所示,我无法从 postman 或除以下来源之外的任何其他来源登录我定义了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM