NullPointerException z niewiadomej przyczyny?

0

Mam zrobione CustomUserDetailsService w nim

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userService.findByUsername(s);

...

i userService.findByUsername(s); działa poprawnie, ale mam również

class TokenAuthenticationService {

    @Autowired
    private UserService userService;

    public void addAuthentication(HttpServletResponse res, String username) {
           userService.findByUsername(username);

...

i tutaj wywala NullPointerException.
TokenAuthenticationService jest używane poprzez

              .addFilterBefore(new JWTLoginFilter("/api/auth", authenticationManager()),
                        UsernamePasswordAuthenticationFilter.class)

następnie

public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
...
    @Override
    protected void successfulAuthentication(
            HttpServletRequest req,
            HttpServletResponse res, FilterChain chain,
            Authentication auth) throws IOException, ServletException {
        TokenAuthenticationService toaus = new TokenAuthenticationService();
        toaus.addAuthentication(res, auth.getName());
    }

Jak to poprawić żeby działało? Moim celem jest zrobić tak aby TokenAuthenticationService pobierał token z bazy dla danego usera i używał go dla JWT, a gdy wygaśnie to zmienić token na nowy czy takie rozwiązanie będzie dobre?

1

Do klasy TokenAuthenticationService wstrzykujesz przez pole UserService (to znaczy chcesz wstrzyknąć). Ale obiekt klasy TokenAuthenticationService tworzysz przez new - czyli spring nie wie nic o tym obiekcie, więc nie może mu wstrzyknąć UserService, więc obiekt klasy TokenAuthenticationService w polu userService przechowuje wartość null, więc wywołanie metody findByUsername wyrzuci NullPointerException.

Żeby działało musisz używać kontenera springa a nie operatora new (żeby móc korzystać z wstrzykiwania zależności). Czyli w skrócie obiekt klasy TokenAuthenticationService musi być beanem, za tym idzie, że obiekt klasy JWTLoginFilter też musi być beanem (bo mu musimy wstrzyknąć tokenAuthenticationService), a w miejscu gdzie rejstrujesz filtr

.addFilterBefore(new JWTLoginFilter("/api/auth", authenticationManager()),
                        UsernamePasswordAuthenticationFilter.class)

Również wstrzykujesz jwtLoginFilter (nie tworzysz przez new).

2

Pięknie. Ten null to normalnie jeden z pierszych przykładów na Springa jaki daję na prezentacjach:

Tylko moja konkluzja jest taka, żeby po prostu odpuścić sobie Springa i magię - bo praca z tak udziwnioną javą , w której new nie działa to po prostu bryndza.

a samo wstrykiwanie pól to jak to napisał Oliver Gierke:

It’s NullpointerExceptions begging to happen

(żródło http://olivergierke.de/2013/11/why-field-injection-is-evil/)

0

Przerobiłem JWTLoginFilter następująco, ale nie mogę znaleźć co zrobić z konstruktorem:

@Component
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {

    private static AuthenticationManager authManager;
    private static String url;

    public void setUrl(String url) {
        JWTLoginFilter.url = url;
    }

    public void setAuthManager(AuthenticationManager authManager) {
        JWTLoginFilter.authManager = authManager;
    }

    @Autowired
    TokenAuthenticationService tokenAuthenticationService;

    public JWTLoginFilter() {
        super(new AntPathRequestMatcher(url));
        setAuthenticationManager(authManager);
    }
...

WebSecurityConfig:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    JWTLoginFilter jwtLoginFilter;

...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        jwtLoginFilter.setUrl("/api/auth");
        jwtLoginFilter.setAuthManager(authenticationManager());
        http.csrf().disable().authorizeRequests()
                .antMatchers("/", "/login", "/api/search/**", "/search/**", "/js/**", "/css/**", "/register").permitAll()
                .antMatchers(HttpMethod.POST, "/api/auth").permitAll()
                .anyRequest().authenticated()
                .and()
                // filter the api/login requests
                .addFilterBefore(jwtLoginFilter,
                        UsernamePasswordAuthenticationFilter.class) 
...

Wywala to BeanCreationException: Error creating bean with name 'JWTLoginFilter' i IllegalArgumentException. Czy w ogóle dobrze wywołuje .addFilterBefore(jwtLoginFilter, ?

Rozwiązanie:

...

    @Autowired
    private UserService userService;
...
            .addFilterBefore(new JWTLoginFilter("/api/auth", authenticationManager(), userService),
                    UsernamePasswordAuthenticationFilter.class);

Dzięki nie ma za co :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1