Spring безопасность 6 - авторизация не работает

Я использую Spring Boot 3 и Spring Security 6 и пытаюсь настроить авторизацию, но не могу заставить ее работать. Я наблюдаю какое-то странное поведение, которое я не могу понять.

Я настроил авторизацию, используя следующий код

http.authorizeHttpRequests(authrorize -> authrorize.requestMatchers("/hello/**").hasRole("USER"));

но после входа в систему получаю ошибку 403 Forbidden.

Я проверил журналы безопасности Spring и вижу в них следующее:

2024-06-15T06:55:20.123+05:30 DEBUG 15236 --- [spring-authn-authz] [nio-8080-exec-4] wc.HttpSessionSecurityContextRepository: получено SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Принципал =com.nagpal. Spring_authn_authz.config.EmployeeUserDetails@2af776cf, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=846F84FABBD0F9B98EFEF4DAAE047AA4], предоставленные полномочия= [ПОЛЬЗОВАТЕЛЬ ]]]

Предоставленные полномочия USER присутствуют, но я все еще получаю ошибку 403.

После некоторого поиска я обнаружил, что к моим полномочиям необходимо добавить «ROLE_», поэтому я попытался добавить префикс «ROLE_» к роли из моего объекта UserDetails:

 @Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    List<GrantedAuthority> authorities = new ArrayList<>();
    log.info("Adding role with ROLE_ appended");
    GrantedAuthority role = new SimpleGrantedAuthority("ROLE_" + employee.getRole());
    authorities.add(role);    
    return authorities;
}

после этого изменения, когда я пытаюсь получить доступ к конечной точке, я все еще получаю ошибку 403 Forbidden, но в журналах я вижу, что префикс ROLE_ добавляется дважды:

2024-06-15T06:59:28.747+05:30 DEBUG 15236 --- [spring-authn-authz] [nio-8080-exec-4] wc.HttpSessionSecurityContextRepository: получено SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Принципал] =com.nagpal. Spring_authn_authz.config.EmployeeUserDetails@2af776cf, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=846F84FABBD0F9B98EFEF4DAAE047AA4], Предоставленные полномочия= [ROLE_ROLE_USER ]]]

Вот код для сотрудникаUserDetails

public class EmployeeUserDetails implements UserDetails {

private Employee employee;

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

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    List<GrantedAuthority> authorities = new ArrayList<>();
    // log.info("Adding role with ROLE_ appended");
    GrantedAuthority role = new SimpleGrantedAuthority(/*"ROLE_" + */ employee.getRole());
    authorities.add(role);    
    return authorities;
}

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

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

и для сотрудника

@Entity
@Data
public class Employee {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "employee_id")
private int id;
@Column
private String email;
@Column
private String password;
@Column
private String role;
}

Создание пользователей:

public void customUsers(UserDetailsManager detailsManager) { 

    Employee employee1 = new Employee();
    employee1.setEmail("[email protected]");
    employee1.setPassword(passwordEncoder().encode("password"));
    employee1.setRole("USER");

    EmployeeUserDetails user1 = new EmployeeUserDetails(employee1);

    detailsManager.createUser(user1);

}

Таблица сотрудников после запуска:

Я не могу понять такое поведение. Если я не добавляю «ROLE_» в свои объекты UserDetails, он вообще не появится, но если я его настрою, он появится дважды.

Может ли кто-нибудь помочь мне понять это?

Возможный дубликат stackoverflow.com/questions/41946473/…

dur 16.06.2024 23:31
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Версия Java на основе версии загрузки
Версия Java на основе версии загрузки
Если вы зайдете на официальный сайт Spring Boot , там представлен start.spring.io , который упрощает создание проектов Spring Boot, как показано ниже.
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
0
1
132
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Присмотревшись к коду hasRole Spring Security, вы увидите, что он автоматически добавляет ROLE_ к значению роли для сравнения.

Поэтому вам необходимо добавить префикс ROLE_ к полномочиям внутри метода getAuthorities.

имеет роль

public AuthorizeHttpRequestsConfigurer<H>.AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
    return this.access(AuthorityAuthorizationManager.hasRole(role));
}

public static <T> AuthorityAuthorizationManager<T> hasRole(String role) {
    Assert.notNull(role, "role cannot be null");
    return hasAuthority("ROLE_" + role);
}

Когда я скопировал ваш код, в моем случае он работал правильно.😱

Сущность

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    List<GrantedAuthority> authorities = new ArrayList<>();
    GrantedAuthority role = new SimpleGrantedAuthority("ROLE_" + this.role);
    log.info("Adding role : " + role.getAuthority());
    authorities.add(role);
    return authorities;
}

бревно

2024-06-15T14:20:01.450+09:00  INFO 22987 --- [nio-8080-exec-6] com.fixadate.global.util.LogAspect       : timeMs = 21
2024-06-15T14:20:01.450+09:00  INFO 22987 --- [nio-8080-exec-6] c.fixadate.domain.member.entity.Member   : Adding role : ROLE_USER

Конфигурация безопасности

.authorizeHttpRequests((req) -> req
    .requestMatchers(PERMIT_URL_ARRAY).permitAll()
    .requestMatchers("/hello/**").hasRole("USER")
    .anyRequest().authenticated())

Чтобы помочь вам, я объясню, как я распределяю роли.

Вы можете легко предоставить полномочия, используя метод commaSeparatedStringToAuthorityList.

Позвольте мне объяснить с помощью кода.

AuthorityUtils.commaSeparatedStringToAuthorityList

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    return AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_" + role);
}

Используя запятую, вы можете легко назначить несколько ролей.

Например, вы можете хранить значения USER,MEMBER в роли сущности. Таким образом, у Сущности будут полномочия ROLE_USER и ROLE_MEMBER. Изучив внутреннюю логику, вы можете увидеть, что она разделяет строку запятыми и добавляет каждую из них в качестве роли.

запятаяSeparatedStringToAuthorityList

public static List<GrantedAuthority> commaSeparatedStringToAuthorityList(String authorityString) {
    return createAuthorityList(StringUtils.tokenizeToStringArray(authorityString, ","));
}

CreateAuthorityList

public static List<GrantedAuthority> createAuthorityList(String... authorities) {
    List<GrantedAuthority> grantedAuthorities = new ArrayList(authorities.length);
    String[] var2 = authorities;
    int var3 = authorities.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        String authority = var2[var4];
        grantedAuthorities.add(new SimpleGrantedAuthority(authority));
    }

    return grantedAuthorities;
}

Решение

Поскольку при добавлении или удалении префикса ROLE_ в методе getAuthorities могут возникнуть непредвиденные проблемы, я решил проблему, добавив префикс ROLE_ в метод setRole.

Сотрудник

public void setRole(String role) { 
    this.role = "ROLE_" + role; 
}

Я так рад, что эта проблема решена.

хорошего дня - Кевин

В моем случае я создал сущность с полем с именем «роль» и сохранил ее. При создании сущности я присвоил полю роли такие значения, как USER и ADMIN.

Kevin 15.06.2024 14:14

Хотя это не рекомендуется, вы можете добавить префикс «ROLE_» при настройке роли. public void setRole(String role) { this.role = "ROLE_" + role; } Тогда вам больше не нужно добавлять префикс «ROLE_» в метод getAuthorities.

Kevin 16.06.2024 04:18

Это сработало. При создании пользователя я добавил префикс ROLE_ к имени роли, и теперь он сохраняется как ROLE_USER в базе данных, а также удалил префикс из метода getAuthorities. Вопрос: это то, чего ожидает Spring (роль в базе данных будет иметь префикс ROLE_)? Вы можете написать это как ответ, и я могу принять это на благо других.

Bagira 16.06.2024 09:37

Я не совсем уверен, ожидает ли Spring именно этого. Однако добавление префикса ROLE_ к ролям в базе данных — это один из подходов, и, безусловно, доступны и другие методы. Лучше всего сравнить различные методы, чтобы найти оптимальный. Я добавлю это к своему предыдущему ответу. Я очень рад, что проблема решена. Хорошего дня - Кевин

Kevin 16.06.2024 10:34

Другие вопросы по теме

Spring Boot 2.5.6 без Spring Security и конфигурации безопасности, обновленной до Spring Boot 3, из-за чего swagger не работает
Какова связь между временем простоя сеанса единого входа Keycloak и временем ожидания сеанса Spring?
Почему нам нужно загружать данные пользователя из БД для каждого запроса при аутентификации JWT с помощью Spring Security?
Перенос org.springframework.security.jwt.Jwt
Миграция Spring Security на Spring Security 6
Как определить предоставленные пользователем полномочия на основе ролей пользователей в Auth0 с помощью Spring Boot
Как я могу пройти аутентификацию, используя тип гранта обмена токенами для олицетворения с помощью Spring Boot и Keycloak?
Какой алгоритм шифрования использует PasswordEncoder Spring Security?
Spring безопасность: как скрыть или обойти страницу «вход с помощью oauth2» с помощью одного клиента oauth при использовании oauth2Login
Как обойти страницу входа в систему keycloak и предоставить предложенному клиентом идентификатору Spring Security

Похожие вопросы

Невозможно имитировать bean-компонент RestClient при тестировании класса, использующего этот bean-компонент
ОШИБКА o.h.e.jdbc.spi.SqlExceptionHelper — ОШИБКА: синтаксическая ошибка на месте «:» или рядом с ним при использовании @Query
Как запустить приложение Spring Boot с распакованными зависимостями?
Существуют ли такие инструменты, как Jqwik, для простого тестирования класса времени Google API? (DateTime, EventDateTime... и т. д.)
Mockito выдает ошибку. Несоответствие аргументов строгой заглушки
Как настроить повторные попытки для неудачных событий приложения Spring, сохраненных в репозитории публикаций событий Spring Modulith?
Java.lang.NoClassDefFoundError: jakarta/servlet/SingleThreadModel — ошибка при использовании apache spark 4.0-preview1
Задержка запроса Spring WebClient из-за неактивного соединения
Какой неустаревший способ установки менеджера транзакций в контейнер прослушивателя - это Spring Kafka 3.2?
Как получить доступ к текущему активному профилю статическим методом?