Не работает авторизация по userDetailsService. Invalid username and password

243
14 апреля 2022, 23:10

WebSecurityConfig.java

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserService userService;
public WebSecurityConfig(UserService userService) {
    this.userService = userService;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
                .antMatchers("/","/registration")
                .permitAll()
                .anyRequest().authenticated()
            .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
            .and()
                .logout()
                .permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userService)
            .passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}

UserService.java

@Service
public class UserService implements UserDetailsService {
private final UserRepo userRepo;
public UserService(UserRepo userRepo) {
    this.userRepo = userRepo;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    return userRepo.findByUsername(username);
}
}

UserRepo.java

public interface UserRepo extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

User.java

@Data
@Entity
@Table(name = "usr")
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String username;
    private String password;
    private boolean active;
    @ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER)
    @CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"))
    @Enumerated(EnumType.STRING)
    private Set<Role> roles;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return getRoles();
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return isActive();
    }
    public boolean isActive() {
        return active;
    }
}

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div th:insert="parts/_header :: header"></div>
<div th:insert="parts/_menu :: menu"></div>
<body>
<div style="margin-left:20px" class="ui mini breadcrumb">
    <a class="section" href="#">Главная</a>
    <i class="right angle icon divider"></i>
    <div class="active section">Авторизация или регистрация</div>
</div>
<div class="ui raised very padded text container segment">
    <h2>Авторизация</h2>
    <div th:if="${param.error}">
        Invalid username and password.
    </div>
    <div th:if="${param.logout}">
        You have been logged out.
    </div>
    <form class="ui form" th:action="@{/login}" method="post">
        <div class="field">
            <label>Имя пользователя</label>
            <input type="text" name="username" placeholder="Имя пользователя"/>
        </div>
        <div class="field">
            <label>Пароль</label>
            <input type="password" name="password" placeholder="Пароль"/>
        </div>
        <button class="ui button" type="submit" style="margin-top: 20px">Войти</button>
        <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
    </form>
    <div class="ui divider"></div>
    <div>
        Нет аккаунта? <a href="/registration"> Зарегистрируйтесь</a>
    </div>

</div>
<div th:insert="parts/_footer :: footer"></div>
</body>
</html>
Answer 1

Я думаю проблема связана с тем, что вы не внедрили зависимости и определить AuthenticationProvider(в нашем случае DaoAuthenticationProvider), настроить или отключить csrf, указать название полей username, password в loginPage которые будем вытаскивать и создавать UsernamePasswordAuthenticationToken:

UserService.java:

    @Component(value = "customUserDetailsService")
    public class UserService implements UserDetailsService {
    private final UserRepo userRepo;
    
    @Autowired
    public UserService(UserRepo userRepo) {
        this.userRepo = userRepo;
    }
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepo.findByUsername(username);
    }
    }
  • внедрить зависимость UserRepo с помощью конструктора через аннотацию @Autowired
  • поменять аннотацию @Service на @Component,т.к. @Service в основном используют для классов описывающих бизнес логику,а @Component более обобщенная аннотация
  • указать название бина @Component(value = "customUserDetailsService"), т.к. помимо вашей есть и другие реализации в Spring(для избежания конфликтов)

WebSecurityConfig.java:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserService userService;
@Autowired
public WebSecurityConfig(@Qualifier("customUserDetailsService") UserService userService) {
    this.userService = userService;
}
   
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/","/registration")
                .permitAll()
                .anyRequest().authenticated()
            .and()
                .formLogin()
                .loginPage("/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
            .and()
                .logout()
                .permitAll();
}

@Bean
public DaoAuthenticationProvider daoAuthProvider() {
    DaoAuthenticationProvider daoAuthProvider = new DaoAuthenticationProvider();
    daoAuthProvider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
    daoAuthProvider.setUserDetailsService(userService);
    return daoAuthProvider;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(daoAuthProvider());
}
}
  • внедряем зависимость UserService userService благодаря аннотации @Autowired над конструктором WebSecurityConfig.

  • с помощью @Qualifier("customUserDetailsService") указываем бин с определенным названием("customUserDetailsService"), который имеет соответствующую реализацию.

  • определяем AuthenticationProvider с помощью которого будет осуществлять аутентификация, в данной ситуации DaoAuthenticationProvider.Рассмотрим подробнее: когда ваш запрос проходит по FilterChain и дойдет до UsernamePasswordAuthenticationFilter, то так как в вашем запросе будут содержаться username и password, он их вытащит и на их основе создаст UsernamePasswordAuthenticationToken(username, password,) после данный объект аутентификации передастся AuthenticationManager, который будет искать подходящий AuthenticationProvider поддерживающий аутентификацию объекта UsernamePasswordAuthenticationToken и наткнется как раз на наш DaoAuhenticationProvider, который в свою очередь использует UserDetailsService - лезет в бд и ищет нашего юзера, на основе данных юзера создаст UserDetails() содержащий username, password,GrantedAuthority. В итоге UserDetailsService возвращает UserDetails и сравнивает данные UserDetails с данными UsernamePasswordAuthenticationToken, если они совпадают то все ок, иначе ошибка.

login.html

Предлагаю отключить csrf если вы не знаете как его настраивть:

  • удалить или закоментить строчку <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />

  • отключить csrf: http.csrf().disable()

READ ALSO
Передача битовой информации по Bluetooth

Передача битовой информации по Bluetooth

я - новичок, так что не судите строгоЯ разрабатываю приложение на Java под Android

149
Работа с Predicate

Работа с Predicate

У меня есть задача, нужно сделать фильтры для стримов с разными типами объектов

255
Не выполняется запрос к API в потоках (Threads)

Не выполняется запрос к API в потоках (Threads)

Пытаюсь сделать тест, имитация работы 2х пользователей над док-мИспользую библиотеки RestAssured и JerseyTest

97
Взаимодействие между контроллерами в JavaFX

Взаимодействие между контроллерами в JavaFX

Подскажите, пожалуйста, как правильно написать взаимодействие между двумя контроллерами

241