1

I have a basic SpringBoot 2.0.5.RELEASE app. Using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR with a restful architecture

I have created this Rest controller for authentication:

@RestController
public class AuthenticationRestController {


    private static final Logger LOG = LoggerFactory.getLogger   (AuthenticationRestController.class);



    @Value("${jwt.header}")
    private String tokenHeader;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserSecurityService userSecurityService;

    @RequestMapping(value = "${jwt.route.authentication.path}", method = RequestMethod.POST)
    public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtAuthenticationRequest authenticationRequest) throws AuthenticationException {

        LOG.info("authenticating [ " + authenticationRequest.getUsername() + " ]");

        authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());

        // Reload password post-security so we can generate the token
        final UserDetails userDetails = userSecurityService.loadUserByUsername(authenticationRequest.getUsername());
        final String token = jwtTokenUtil.generateToken(userDetails);

        // Return the token
        return ResponseEntity.ok(new JwtAuthenticationResponse(token));
    }

    @RequestMapping(value = "${jwt.route.authentication.refresh}", method = RequestMethod.GET)
    public ResponseEntity<?> refreshAndGetAuthenticationToken(HttpServletRequest request) {
        String authToken = request.getHeader(tokenHeader);
        final String token = authToken.substring(7);
        String username = jwtTokenUtil.getUsernameFromToken(token);
        JwtUser user = (JwtUser) userSecurityService.loadUserByUsername(username);

        if (jwtTokenUtil.canTokenBeRefreshed(token, user.getLastPasswordResetDate())) {
            String refreshedToken = jwtTokenUtil.refreshToken(token);
            return ResponseEntity.ok(new JwtAuthenticationResponse(refreshedToken));
        } else {
            return ResponseEntity.badRequest().body(null);
        }
    }

    @ExceptionHandler({AuthenticationException.class})
    public ResponseEntity<String> handleAuthenticationException(AuthenticationException e) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
    }

    /**
     * Authenticates the user. If something is wrong, an {@link AuthenticationException} will be thrown
     */
    private void authenticate(String username, String password) {
        Objects.requireNonNull(username);
        Objects.requireNonNull(password);

        try {
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException e) {
            throw new AuthenticationException("User is disabled!", e);
        } catch (BadCredentialsException e) {
            throw new AuthenticationException("Bad credentials!", e);
        }
    }
}

and this configuration file to manage the configuration

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

    //private static final Logger LOG = LoggerFactory.getLogger(ApiWebSecurityConfig.class);

    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;


    @Autowired
    private UserSecurityService userSecurityService;

    @Value("${jwt.header}")
    private String tokenHeader;

    @Value("${jwt.route.authentication.path}")
    private String authenticationPath;



    @Value("${server.servlet.context-path}")
    private String serverContextPath;

    /** The encryption SALT. */
    private static final String SALT = "f13333";


    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userSecurityService)
                .passwordEncoder(passwordEncoder());
    }


    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12, new SecureRandom(SALT.getBytes()));
    }


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


    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {

         httpSecurity
         // we don't need CSRF because our token is invulnerable
         .csrf().disable()

         .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()

         // don't create session
         .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
         .authorizeRequests()

         // Un-secure H2 Database
         .antMatchers("/h2-console/**/**").permitAll()
         .antMatchers("/auth/**").permitAll()
         .anyRequest().authenticated();


     // Custom JWT based security filter
         JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(userDetailsService(), jwtTokenUtil, tokenHeader);
         httpSecurity
             .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);


     // disable page caching
     httpSecurity
         .headers()
         .frameOptions().sameOrigin()  // required to set for H2 else H2 Console will be blank.
         .cacheControl();
    }


    @Override
    public void configure(WebSecurity web) throws Exception {
        // AuthenticationTokenFilter will ignore the below paths
        web
            .ignoring()

            .antMatchers(
                HttpMethod.POST,
                authenticationPath
            )
    }
}

but when I put this curl in the console of my computer, I have this error in the server console hosted in a Ubuntu server:

url -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d '{"username":"nunet@gmail.com","password":"qwerty"}' "http://139.262.221.117:1234/calssada/api/v1/auth"

the server log:

2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG o.a.coyote.http11.Http11InputBuffer - Received [POST /calssada/api/v1/auth HTTP/1.1
Host: 139.262.221.117:1234
User-Agent: curl/7.54.0
Accept: */*
Content-Type: application/json
Cache-Control: no-cache
Content-Length: 56

{"username":"nunet@gmail.com","password":"qwerty"}]
2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG o.a.c.a.AuthenticatorBase - Security checking request POST /calssada/api/v1/auth
2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG org.apache.catalina.realm.RealmBase -   No applicable constraints defined
2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG o.a.c.a.AuthenticatorBase -  Not subject to any constraint
2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG o.a.tomcat.util.http.Parameters - Set encoding to UTF-8
2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG o.a.c.c.C.[Tomcat].[localhost] - Processing ErrorPage[errorCode=0, location=/error]
2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG o.a.c.c.C.[.[.[.[dispatcherServlet] -  Disabling the response for further output
2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG o.a.t.util.net.SocketWrapperBase - Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@2fca076b:org.apache.tomcat.util.net.NioChannel@5c80e3a0:java.nio.channels.SocketChannel[connected local=/139.262.221.117:1234 remote=/119.88.31.26:58071]], Read from buffer: [0]
2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG o.a.tomcat.util.net.NioEndpoint - Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@2fca076b:org.apache.tomcat.util.net.NioChannel@5c80e3a0:java.nio.channels.SocketChannel[connected local=/139.262.221.117:1234 remote=/119.88.31.26:58071]], Read direct from socket: [0]
2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG o.a.coyote.http11.Http11Processor - Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@2fca076b:org.apache.tomcat.util.net.NioChannel@5c80e3a0:java.nio.channels.SocketChannel[connected local=/139.262.221.117:1234 remote=/119.88.31.26:58071]], Status in: [OPEN_READ], State out: [OPEN]
2018-10-15 19:14  [http-nio-1234-exec-1] DEBUG o.a.coyote.http11.Http11NioProtocol - Pushed Processor [org.apache.coyote.http11.Http11Processor@28de9baa]
2018-10-15 19:14  [http-nio-1234-exec-2] DEBUG o.a.coyote.http11.Http11NioProtocol - Processing socket [org.apache.tomcat.util.net.NioChannel@5c80e3a0:java.nio.channels.SocketChannel[connected local=/139.262.221.117:1234 remote=/119.88.31.26:58071]] with status [OPEN_READ]
2018-10-15 19:14  [http-nio-1234-exec-2] DEBUG o.a.coyote.http11.Http11NioProtocol - Found processor [null] for socket [org.apache.tomcat.util.net.NioChannel@5c80e3a0:java.nio.channels.SocketChannel[connected local=/139.262.221.117:1234 remote=/119.88.31.26:58071]]
2018-10-15 19:14  [http-nio-1234-exec-2] DEBUG o.a.coyote.http11.Http11NioProtocol - Popped processor [org.apache.coyote.http11.Http11Processor@28de9baa] from cache
2018-10-15 19:14  [http-nio-1234-exec-2] DEBUG o.a.t.util.net.SocketWrapperBase - Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@2fca076b:org.apache.tomcat.util.net.NioChannel@5c80e3a0:java.nio.channels.SocketChannel[connected local=/139.262.221.117:1234 remote=/119.88.31.26:58071]], Read from buffer: [0]
2018-10-15 19:14  [http-nio-1234-exec-2] DEBUG o.a.coyote.http11.Http11Processor - Error parsing HTTP request header
java.io.EOFException: null
    at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1289)
    at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1223)
    at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:729)
    at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:368)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
2018-10-15 19:14  [http-nio-1234-exec-2] DEBUG o.a.coyote.http11.Http11Processor - Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@2fca076b:org.apache.tomcat.util.net.NioChannel@5c80e3a0:java.nio.channels.SocketChannel[connected local=/139.262.221.117:1234 remote=/119.88.31.26:58071]], Status in: [OPEN_READ], State out: [CLOSED]
Nuñito Calzada
  • 4,394
  • 47
  • 174
  • 301
  • why is your Content-Length 56? that curl command should yield a Content-Length of 50. and where does that stray `]` at the end come from? is it possible that you get invisible unprintable characters in your terminal for the -d argument somehow? what terminal do you use? – hanshenrik Oct 19 '18 at 19:13
  • 1
    start a netcat server and record the ouput (`nc -l 9999 > output.txt`) then change the url to `http://127.0.0.1:9999/calssada/api/v1/auth` and run curl, then inspect output.txt created by netcat in a hex editor (like the [HxD Hex Editor](https://mh-nexus.de/en/hxd/) for Windows, or [Bless Hex Editor](https://github.com/bwrsandman/Bless) for Linux) and look for invisible characters in your json post argument to check the above.. – hanshenrik Oct 19 '18 at 19:17
  • "url -X POST ..." Initial "c" in "curl" has been lost. – Konstantin Kolinko Oct 23 '18 at 03:06

1 Answers1

2

Maybe you have to add -H "Connection: close" to your curl command?

Note that in your error log there are two requests. The first one is processed by thread "http-nio-1234-exec-1". I wonder whether "Processing ErrorPage[errorCode=0, location=/error]" means that it resulted in a failure.

HTTP/1.1 protocol allows sending several requests over the same connection (aka Keep-Alive). As there is no "Connection: close" header, Tomcat continues to read from the connection. This is performed by thread "http-nio-1234-exec-2" and fails with an EOF when trying to read the first line of the request (Http11InputBuffer.parseRequestLine()). The EOF is expected here.

Konstantin Kolinko
  • 3,854
  • 1
  • 13
  • 21
  • This is a normal error and NON-ISSUE as per https://stackoverflow.com/questions/51501360/springboot-error-parsing-http-request-header-oauth2-https-endpoints/54905370#comment109532181_54905370 – logixplayer May 21 '20 at 06:58