Home > Software design >  NullPointerException when getting username from principal
NullPointerException when getting username from principal

Time:10-28

When I try to get user name on backend

@CrossOrigin(origins = "*")
@RestController
@RequestMapping("/persons")
public class PersonController {
...

@PostMapping
    public ResponseEntity<PersonDto> addPerson(@RequestBody PersonDto objectDto, @AuthenticationPrincipal Principal principal){
System.out.println(principal.getName());

I got an error

2021-10-27 23:30:39.309 ERROR 10744 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

I can see that frontent is sending JWT token (don't look at 'accessToken' I added it manually to header).

POST /persons HTTP/1.1
Host: localhost:8081
Connection: keep-alive
Content-Length: 217
sec-ch-ua: "Chromium";v="94", "Google Chrome";v="94", ";Not A Brand";v="99"
accessToken: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJHaWVuZWsiLCJpYXQiOjE2MzUzNjg1MjQsImV4cCI6MTYzNTQ1NDkyNH0.leuqnc-8fHNBVhTmukruom-RudxicWP3ykkMyMiapwY8bBVCFLwlNssXNK-gyo0RHig9d-dg83-QG9LDqVO9VA
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36
Content-Type: application/json
Accept: application/json, text/plain, */*
x-access-token: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJHaWVuZWsiLCJpYXQiOjE2MzUzNjg1MjQsImV4cCI6MTYzNTQ1NDkyNH0.leuqnc-8fHNBVhTmukruom-RudxicWP3ykkMyMiapwY8bBVCFLwlNssXNK-gyo0RHig9d-dg83-QG9LDqVO9VA
sec-ch-ua-platform: "Windows"
Origin: http://localhost:4200
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:4200/
Accept-Encoding: gzip, deflate, br
Accept-Language: pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7

What is the reason that backend does not see it?

Attaching Security Config - please let me know if you need more details

package pl.portal.randkowy.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.core.userdetails.UserDetailsService;

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

    @Autowired
    UserDetailsService userDetailsService;

    @Autowired
    private AuthEntryPointJwt unauthorizedHandler;

    @Bean
    public AuthTokenFilter authenticationJwtTokenFilter() {
        return new AuthTokenFilter();
    }

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

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

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests().antMatchers("/api/auth/**").permitAll()
                .antMatchers("/api/test/**").permitAll()
                .antMatchers("/persons").permitAll()
                .antMatchers("/persons/**").permitAll()
                .antMatchers("/preferences/**").permitAll()
                .antMatchers("/interests/**").permitAll()
                .antMatchers("/secretdata/**").permitAll()
                .anyRequest().authenticated();

        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }

}

When I am trying to check this way

@PostMapping
    public ResponseEntity<PersonDto> addPerson(@RequestBody PersonDto objectDto, Principal principal){
        //zalogowany użytkownik
        Object principal2 = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        System.out.println("--------------------------------------*****************");
        if (principal2 instanceof UserDetailsImpl) {
            String username = ((UserDetailsImpl)principal2).getUsername();
            System.out.println(username);
        } else {
            String username = principal2.toString();
            System.out.println(username);
        }
        System.out.println("--------------------------------------*****************");

I got

--------------------------------------*****************
anonymousUser
--------------------------------------*****************

Why Spring Security does not read this token? It is sended to frontend after login procedure, so it should be fine. Spring should get in in header from this x-access-token option?

EDIT: I found out during debugging that jwt is null, but why - I can see that request has inside jwt token from frontend?

jwt - null in request, jwt visible

CodePudding user response:

Debug into your parseJwt(request) to see how your application extracts the token from the request. The practice for the access token is: it's usually in the Authorization header and starts with Bearer

Request headers

Authorization: Bearer access-token

CodePudding user response:

try changing the webSecurityConfig like this

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().csrf().disable()
            .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests().antMatchers("/api/auth/**").permitAll()
            .antMatchers("/api/test/**").permitAll()
            .antMatchers("/persons/**").permitAll()
            .antMatchers("/preferences/**").permitAll()
            .antMatchers("/interests/**").permitAll()
            .antMatchers("/secretdata/**").permitAll()
            .anyRequest().authenticated();

    http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
  • Related