spring security + jwt loadUserByUsername not call

Viewed 57

[SecurityConfig]

@Slf4j
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
    
    private final AuthenticationConfiguration authenticationConfiguration;
    
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {

        httpSecurity
                .cors()
                .and()
                .csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                /*.headers().disable()*/
                .anyRequest().permitAll()
                .and()
                .addFilterBefore(new JwtAuthenticationFilter(authenticationManager(authenticationConfiguration)), UsernamePasswordAuthenticationFilter.class);
                
        return httpSecurity.build();
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
}

[JwtAuthenticationFilter]

@RequiredArgsConstructor
@Slf4j
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;

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

        log.info("Check Run");
        ObjectMapper om = new ObjectMapper();
        try {

            AdvertiserUserDto.Read read = om.readValue(request.getInputStream(), AdvertiserUserDto.Read.class);

            UsernamePasswordAuthenticationToken authenticationToken
                    = new UsernamePasswordAuthenticationToken(read.getUserLoginId(), read.getPassword());
            
            log.info(authenticationToken.getPrincipal().toString());
            log.info(authenticationToken.getCredentials().toString());

            return authenticationManager.authenticate(authenticationToken);
        }
        catch (IOException e) {
            e.printStackTrace();
            log.error("IO Exception");
        }
        return null;
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        log.info("Success");
        super.successfulAuthentication(request, response, chain, authResult);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        log.info("Fail");
        super.unsuccessfulAuthentication(request, response, failed);
    }
}

[SecurityUserDetailsService]

@Slf4j
@RequiredArgsConstructor
@Service
public class SecurityUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    @Transactional
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        log.info("loadUserByUsername"); // **not Run**
        UserEntity user = userRepository.findByUserLoginId(username).orElseThrow(() -> new UsernameNotFoundException("no Search User"));

        return new SecurityUserDetails(
                user,
                /*Collections.singleton(new SimpleGrantedAuthority("ROLE_" + user.getType().getRoll()))*/
                Collections.singleton(new SimpleGrantedAuthority("ROLE_" + "USER"))
        );
    }
}

[SecurityUserDetails]

@RequiredArgsConstructor
@Data
public class SecurityUserDetails implements UserDetails {

    private final UserEntity user;
    private final Collection<? extends GrantedAuthority> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUserId().toString();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

}

Please note that I am struggling at a very basic level.

Here's the process I've tried so far:

  1. Login attempt (user, pw)

  2. Security Chain is executed

  3. A filter that inherits from UsernamePasswordAuthenticationFilter is executed

  4. It has been confirmed that ID and PW are normally passed as parameters.

  5. Execute the authenticate() method of AuthenticationManager to check if ID and PW are correct.

  6. The loadUserByUsername() method, which inherited UserDeatilsService, is not executed, and immediately the unsuccessfulAuthentication() method is executed and fails.

I attach the code.

According to my plan, validation should be done via loadUserByUsername().

But why does the validation job fail immediately without even being executed?

Result :

c.b.k.c.c.t.JwtAuthenticationFilter      : Check Run
c.b.k.c.c.t.JwtAuthenticationFilter      : test
c.b.k.c.c.t.JwtAuthenticationFilter      : 1234
c.b.k.c.c.t.JwtAuthenticationFilter      : Fail
1 Answers

I answer myself how I found it.

@Slf4j
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final SecurityUserDetailsService securityUserDetailsService;

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {

        AuthenticationManagerBuilder authenticationManagerBuilder = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.userDetailsService(securityUserDetailsService);
        AuthenticationManager authenticationManager = authenticationManagerBuilder.build();

        httpSecurity
                .cors()
                .and()
                .csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .authenticationManager(authenticationManager)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                /*.headers().disable()*/
                .anyRequest().permitAll()
                .and()
                .addFilterBefore(new JwtAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);

        return httpSecurity.build();
    }

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

}

Here's how I discovered and succeeded by other advice.

I think there are some things wrong with this method or something that needs to be improved.

I don't know if it's the right way, but I'm writing it down for others and for improvement.

AuthenticationManagerBuilder authenticationManagerBuilder = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.userDetailsService(securityUserDetailsService);
        AuthenticationManager authenticationManager = authenticationManagerBuilder.build();

The key is here.

I approached the problem in that the userDetatilsService that I have customized and extended cannot be informed (cannot be injected) to the AuthenticationManager.

Related