Spring Boot Form Login Пользовательская авторизация UserDetailsService не работает

Я новичок в Spring Boot и действительно изо всех сил пытаюсь это понять. Я не нахожу API-интерфейс безопасности Spring boot очень интуитивным, но я пытаюсь его получить.

Я собираюсь использовать MySql с JPA для получения пользователей из базы данных, но сначала просто для того, чтобы убедиться, что леса безопасности работают, я просто жестко кодирую пользователя из службы сведений о сотрудниках.

После долгих хлопот мне удалось заставить работать базовую аутентификацию, но авторизация не работает. Я отредактировал вопрос, чтобы отразить основную проблему:

Я знаю, что есть другие вопросы, прежде чем люди отметят это как дубликат, я просмотрел все из них, все они дают базовые ответы, которые, как мне кажется, уже работают в моем приложении.

В конце /home путь для домашнего метода, /mama путь для роли мама

Вот код:

//AppSecurityConfig.java
package com.straightwalls.absencemanagement.config;

import com.straightwalls.absencemanagement.service.EmployeeDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import 
     org.springframework.security.config
    .annotation.authentication.builders.AuthenticationManagerBuilder;
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.core.userdetails.UserDetailsService;

import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

   protected void configure(HttpSecurity http) throws Exception {
    //Disabled for development
    http.authorizeRequests()
            .antMatchers("/mama").hasRole("MAMA")
            .antMatchers("/home").hasAnyRole("ROLE_HEAD", "ROLE_MAMA")
            .and()
            .formLogin();
}

    @Bean
    /*
    * Returning no op password encoder for now,
    * as we are not encoding passwords as no registration
    * implemented for Prototype. We would need to add the users from a separate service. W
    *
    * */
    public PasswordEncoder getPasswordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

}



//EmployeeDetailsService
package com.straightwalls.absencemanagement.service;

import com.straightwalls.absencemanagement.model.Employee;
import com.straightwalls.absencemanagement.model.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Service
public class EmployeeDetailsService implements UserDetailsService {


    @Override
    
    public UserDetails loadUserByUsername(String username)
                         throws UsernameNotFoundException {
         
    if (!username.equals("Mama")){
        throw new UsernameNotFoundException(
            "You got the wrong Username, should be mama"
        );
    }

    Employee employee = new Employee();
    Role role = new Role();
    role.setName("HEAD");
    employee
        .setUsername(username)
        .setPassword("1234")
        .setRole(role);

    return new EmployeePrincipal(employee);
    }
}


    //EmployeePrincipal.java
    package com.straightwalls.absencemanagement.service;
    
    import com.straightwalls.absencemanagement.model.Employee;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    import java.util.Arrays;
    import java.util.Collection;
    
    
    public class EmployeePrincipal implements UserDetails {

    private Employee employee;
    
    //Added another default Constructor, incase
    public EmployeePrincipal(){

    }

    public EmployeePrincipal(Employee employee){
        this.employee = employee;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_HEAD"));
        //return Arrays.asList(new SimpleGrantedAuthority("ROLE_" + employee.getRole().getName()));
    }

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

    @Override
    public String getUsername() {
        return employee.getUsername();
    }

    /*
    * Methods below are the rubbish methods, we keep as true for now
    *
    * */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

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

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

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



//Login.java
package com.straightwalls.absencemanagement.api;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginApi {

    @RequestMapping("/")
    public String index(){
        return "Straight Walls absence management!";
    }

    @RequestMapping("/home")
    public String home(){
        return "Welcome to Home!";
    }

    /**
    * This method can be deleted in the end
    *
    */
    @RequestMapping("/mama")
    public String roleTest(){
        return "This end point is only for Mama!";
    }

}

//Employee.java
package com.straightwalls.absencemanagement.model;

import org.hibernate.annotations.Fetch;

import javax.persistence.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@Entity
public class Employee {

    @Id
    private long id;
    private String firstName;
    private String lastName;
    private String username;
    private String password;
    private String startDate;

    @ManyToOne
    @JoinColumn(name = "roleId", referencedColumnName = "id")
    private Role role;

    @ManyToOne
    @JoinColumn(name = "departmentId", referencedColumnName = "id")
    private Department department;

    public Employee(){

    }

    public long getId() {
        return id;
    }

    public Employee setId(long id) {
        this.id = id;
        return this;
    }

    public String getFirstName() {
        return firstName;
    }

    public Employee setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public String getLastName() {
        return lastName;
    }

    public Employee setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

    public String getUsername() {
        return username;
    }

    public Employee setUsername(String username) {
        this.username = username;
        return this;
    }

    public String getPassword() {
        return password;
    }

    public Employee setPassword(String password) {
        this.password = password;
        return this;
    }

    public String getStartDate() {
        return startDate;
    }


    public Employee setStartDate(LocalDate startDate) {
      this.startDate =   
      startDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
      return this;
    }

    public Role getRole() {
        return role;
    }

    public Employee setRole(Role role) {
        this.role = role;
        return this;
    }

    public Department getDepartment() {
        return department;
    }

    public Employee setDepartment(Department department) {
        this.department = department;
        return this;
    }
}

Я вхожу в систему с фиктивной ролью HEAD. Таким образом, я должен иметь доступ: /, /home, но не /mama. Я не могу получить доступ к /home, даже если роль была правильно определена.

В моем базовом пакете у меня есть следующие пакеты: config: с этим файлом конфигурации, модель: API всех сущностей: репозиторий контроллеров: репозитории (еще не используются) служба: где у меня есть класс EmployeeDetailsService и EmployeeDetails

Буду очень признателен за любой совет, так как я ожидаю, что это сработает, но он просто не выдает никаких ошибок и просто постоянно говорит о неверных учетных данных, хотя я ввел имя пользователя Mama и пароль Mama и попытался использовать NoOpPasswordEncoder.

Удалось исправить это, очистив код и внеся несколько изменений в метод grantAuthorities, который не нуждался в ROLE_


person ptmp_727    schedule 20.04.2021    source источник
comment
Спасибо тебе за это. Я прохожу пример. Да, я сделал этот класс EmployeeDetailsService, он есть в примере кода. Я пробовал все. В памяти работает нормально, но это не то, что я хочу   -  person Codemaster    schedule 20.04.2021
comment
автопроводка _1_ вместо _2_   -  person ptmp_727    schedule 20.04.2021
comment
также мне удалось найти пример github.com/anushshetty1234/BookStore/blob/master/BookStore/src/   -  person Codemaster    schedule 20.04.2021
comment
не могли бы вы запустить свое приложение с активными журналами отладки, запустить его, выполнить запрос и обновить свой вопрос с помощью журналов. stackoverflow.com/a/47729991/1840146   -  person Codemaster    schedule 20.04.2021
comment
Хорошо, я запущу приложение с логами, я попробовал автоматическое подключение EmployeeDetailsService, то же самое.   -  person Toerktumlare    schedule 21.04.2021
comment
Мне удалось заставить работать базовую безопасность, теперь об авторизации   -  person ptmp_727    schedule 21.04.2021
comment
Журналы сервера: 2021-04-21 07:42:12.151 INFO 10370 --- [restartedMain] traightWallsAbsenceManagementApplication: запуск StraightWallsAbsenceManagementApplication с использованием Java 16 на Peshotans-MacBook-Air.local с PID 10370 (/Users/peshotanpavri/java/IdeaManageWallProjectsA/StraightManagementApplication /target/classes, начатые peshotanpavri в /Users/peshotanpavri/java/IdeaProjects/StraightWallsAbsenceManagement) 2021-04-21 07:42:12.154 INFO 10370 --- [restartedMain] traightWallsAbsenceManagementApplication: активный профиль не установлен, возвращаемся к профилям по умолчанию: default 2021-04-21 07:42:12.254 INFO 10370 --- [restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : свойства Devtools по умолчанию активны! Установите для Spring.devtools.add-properties значение false, чтобы отключить level.web» в «DEBUG» 2021-04-21 07:42:14.065 ИНФОРМАЦИЯ 10370 --- [restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Начальная загрузка репозиториев Spring Data JPA в режиме DEFAULT. 2021-04-21 07:42:14.203 ИНФОРМАЦИЯ 10370 --- [restartedMain] .s.d.r.c.RepositoryConfigurationDelegate: завершено сканирование репозитория данных Spring за 113 мс. Найдено 1 интерфейс репозитория JPA. 2021-04-21 07:42:15.235 ИНФОРМАЦИЯ 10370 --- [restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat инициализирован с портом(ами): 8080 (http) 21-04-2021 07:42:15.253 ИНФОРМАЦИЯ 10370 - -- [restartedMain] o.apache.catalina.core.StandardService : запуск службы [Tomcat] 21-04-2021 07:42:15.253 INFO 10370 --- [restartedMain] org.apache.catalina.core.StandardEngine : запуск сервлета двигатель: [Apache Tomcat/9.0.45] 2021-04-21 07:42:15.404 INFO 10370 --- [restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] : Инициализация Spring встроенного WebApplicationContext 2021-04-21 07:42:15.405 INFO 10370 --- [restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: инициализация завершена за 3147 мс HikariPool-1 — Запуск... 2021-04-21 07:42:16.670 INFO 10370 --- [restartedMain] com.zaxxer.hikari.HikariDataSource: HikariPool-1 — Запуск завершен. 2021-04-21 07:42:16.743 ИНФОРМАЦИЯ 10370 --- [restartedMain] o.hibernate.jpa.internal.util.LogHelper: HHH000204: Обработка PersistenceUnitInfo [имя: по умолчанию] 2021-04-21 07:42:16. 859 INFO 10370 --- [restartedMain] org.hibernate.Version: HHH000412: основная версия Hibernate ORM 5.4.30.Final 2021-04-21 07:42:17.121 INFO 10370 --- [restartedMain] o.hibernate.annotations. common.Version: HCANN000001: Hibernate Commons Annotations {5.1.2.Final} 2021-04-21 07:42:17.327 INFO 10370 --- [restartedMain] org.hibernate.dialect.Dialect: HHH000400: Использование диалекта: org.hibernate .dialect.MySQL8Dialect 21-04-2021 07:42:18.359 INFO 10370 --- [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator: HHH000490: использование реализации JtaPlatform: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] 202 04-21 07:42:18.383 INFO 10370 --- [restartedMain] j.LocalContainerEntityManagerFactoryBean : Инициализирован JPA EntityManagerFactory для модуля сохраняемости "по умолчанию" : spring.jpa.open-in-view включен по умолчанию. Следовательно, запросы к базе данных могут выполняться во время рендеринга вида. Явно настройте spring.jpa.open-in-view, чтобы отключить это предупреждение. web.context.request.async.WebAsyncManagerIntegrationFilter@7e4d562b, org.springframework.security.web.context.SecurityContextPersistenceFilter@657e3908, org.springframework.security.web.header.HeaderWriterFilter@484dbda9, org.springframework.security.web.csrf. CsrfFilter@358c0253, org.springframework.security.web.authentication.logout.LogoutFilter@4596a3b, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@6dceb9b9, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter40, 697 org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@6a227727, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@166853dc, org.springframework.security.web.savedreq uest.RequestCacheAwareFilter@185f798b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@7f7692e4, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5406fd30, org.springframework.security.web23,[email protected] springframework.security.web.access.ExceptionTranslationFilter@44bb34bf, org.springframework.security.web.access.intercept. FilterSecurityInterceptor@768269d7] 21-04-2021 07:42:18.981 INFO 10370 --- [restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor : Инициализация ExecutorService 'applicationTaskExecutor' 21-04-2021 07:42:19.784 INFO-10370 --] [restartedMain] o.s.b.d.a.OptionalLiveReloadServer : сервер LiveReload работает на порту 35729 21-04-2021 07:42:19.835 INFO 10370 --- [restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat запущен на порту(ах): 8080 (http) с контекстным путем '' 2021-04-21 07:42:19.851 INFO 10370 --- [restartedMain] traightWallsAbsenceManagementApplication : запуск StraightWallsAbsenceManagementApplication за 8,486 секунды (JVM работает для 10,642) 2021-04-21 07:42:45.273 INFO-1037 -8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Инициализация Spring DispatcherServlet 'dispatcherServlet' 2021-04-21 07:42:45.275 INFO 10370 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Инициализация сервлета «dispatcherServlet» 2021-04-2 1 07:42:45.300 INFO 10370 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet: инициализация завершена за 24 мс   -  person ptmp_727    schedule 21.04.2021


Ответы (1)


Также изменили hasRole, hasAnyRole с hasAuthority и hasAnyAuthority, убрав ROLE_ Не знаю почему, но теперь это работает.

Кроме того, теперь API намного проще для понимания.

Вот обновленный код:

Вы создали класс, реализующий

@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(userDetailsService);

        /*auth.inMemoryAuthentication()
                .withUser("appuser").password("1234").roles("HEAD")
                .and()
                .withUser("Mama").password("Mama").roles("MAMA");*/

    }

    @Override
    /*
    * Now we have learnt the basics of Spring Security & Authrization method is completed.
    * Lets fix Authentication first!
    * Got it to work with hasAuthority & hasAnyAuthority but not with roles, not sure why, but it works atm
    *
    * */
    protected void configure(HttpSecurity http) throws Exception {
        //Disabled for development
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/mama").hasAuthority("MAMA")
                .antMatchers("/home").hasAnyAuthority("HEAD", "MAMA")
                .anyRequest().authenticated()

                .and()
                .formLogin()
                    .loginPage("/")
                    .defaultSuccessUrl("/home");
    }

    @Bean
    /*
    * Returning no op password encoder for now, as we are not encoding passwords as no registration
    * implemented for Prototype. We would need to add the users from a separate service. W
    *
    * */
    public PasswordEncoder getPasswordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

}


//EmployeeDetailsService.java
@Service
public class EmployeeDetailsService implements UserDetailsService {


    @Override
    /*
    * First, we are testing the Employee details service, independent of the Database, just to make sure we have this part working,
    * For the purpose of these prototypes, we wont use password encoder because we are not registering,
    *
    * */
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        if (!username.equals("Mama")){
            throw new UsernameNotFoundException("You got the wrong Username, should be mama");
        }

        Employee employee = new Employee();
        Role role = new Role();
        role.setName("HEAD");
        employee
                .setUsername(username)
                .setPassword("1234")
                .setRole(role);

        return new EmployeePrincipal(employee);

    }
}


    //EmployeePrincipal.java
    public class EmployeePrincipal implements UserDetails {
    
        private Employee employee;
    
    
        public EmployeePrincipal(Employee employee){
            this.employee = employee;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
            authorities.add(new SimpleGrantedAuthority(employee.getRole().getName()));
            return  authorities;
        }
    
        @Override
        public String getPassword() {
            return employee.getPassword();
        }
    
        @Override
        public String getUsername() {
            return employee.getUsername();
        }
    
        /*
        * Methods below are the rubbish methods, we keep as true for now
        *
        * */
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            return true;
        }
    }


@RestController
public class LoginApi {

    @RequestMapping("/")
    public String index(){
        return "Index"
    }

    @RequestMapping("/home")
    public String home(){
        return "Home!";
    }

    /*
    * This method can be deleted in the end
    * */
    @RequestMapping("/mama")
    public String roleTest(){
        return "This end point is only for Mama!";
    }

}
, чтобы переопределить _2_? См. этот marcobehler.com/guides/spring-security.

@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(userDetailsService);

        /*auth.inMemoryAuthentication()
                .withUser("appuser").password("1234").roles("HEAD")
                .and()
                .withUser("Mama").password("Mama").roles("MAMA");*/

    }

    @Override
    /*
    * Now we have learnt the basics of Spring Security & Authrization method is completed.
    * Lets fix Authentication first!
    * Got it to work with hasAuthority & hasAnyAuthority but not with roles, not sure why, but it works atm
    *
    * */
    protected void configure(HttpSecurity http) throws Exception {
        //Disabled for development
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/mama").hasAuthority("MAMA")
                .antMatchers("/home").hasAnyAuthority("HEAD", "MAMA")
                .anyRequest().authenticated()

                .and()
                .formLogin()
                    .loginPage("/")
                    .defaultSuccessUrl("/home");
    }

    @Bean
    /*
    * Returning no op password encoder for now, as we are not encoding passwords as no registration
    * implemented for Prototype. We would need to add the users from a separate service. W
    *
    * */
    public PasswordEncoder getPasswordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

}


//EmployeeDetailsService.java
@Service
public class EmployeeDetailsService implements UserDetailsService {


    @Override
    /*
    * First, we are testing the Employee details service, independent of the Database, just to make sure we have this part working,
    * For the purpose of these prototypes, we wont use password encoder because we are not registering,
    *
    * */
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        if (!username.equals("Mama")){
            throw new UsernameNotFoundException("You got the wrong Username, should be mama");
        }

        Employee employee = new Employee();
        Role role = new Role();
        role.setName("HEAD");
        employee
                .setUsername(username)
                .setPassword("1234")
                .setRole(role);

        return new EmployeePrincipal(employee);

    }
}


    //EmployeePrincipal.java
    public class EmployeePrincipal implements UserDetails {
    
        private Employee employee;
    
    
        public EmployeePrincipal(Employee employee){
            this.employee = employee;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
            authorities.add(new SimpleGrantedAuthority(employee.getRole().getName()));
            return  authorities;
        }
    
        @Override
        public String getPassword() {
            return employee.getPassword();
        }
    
        @Override
        public String getUsername() {
            return employee.getUsername();
        }
    
        /*
        * Methods below are the rubbish methods, we keep as true for now
        *
        * */
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            return true;
        }
    }


@RestController
public class LoginApi {

    @RequestMapping("/")
    public String index(){
        return "Index"
    }

    @RequestMapping("/home")
    public String home(){
        return "Home!";
    }

    /*
    * This method can be deleted in the end
    * */
    @RequestMapping("/mama")
    public String roleTest(){
        return "This end point is only for Mama!";
    }

}
person ptmp_727    schedule 21.04.2021