Я использую 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, он вообще не появится, но если я его настрою, он появится дважды.
Может ли кто-нибудь помочь мне понять это?
Присмотревшись к коду 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.
Позвольте мне объяснить с помощью кода.
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_" + role);
}
Используя запятую, вы можете легко назначить несколько ролей.
Например, вы можете хранить значения USER,MEMBER
в роли сущности. Таким образом, у Сущности будут полномочия ROLE_USER
и ROLE_MEMBER
.
Изучив внутреннюю логику, вы можете увидеть, что она разделяет строку запятыми и добавляет каждую из них в качестве роли.
public static List<GrantedAuthority> commaSeparatedStringToAuthorityList(String authorityString) {
return createAuthorityList(StringUtils.tokenizeToStringArray(authorityString, ","));
}
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
.
Хотя это не рекомендуется, вы можете добавить префикс «ROLE_» при настройке роли. public void setRole(String role) { this.role = "ROLE_" + role; }
Тогда вам больше не нужно добавлять префикс «ROLE_» в метод getAuthorities.
Это сработало. При создании пользователя я добавил префикс ROLE_ к имени роли, и теперь он сохраняется как ROLE_USER в базе данных, а также удалил префикс из метода getAuthorities. Вопрос: это то, чего ожидает Spring (роль в базе данных будет иметь префикс ROLE_)? Вы можете написать это как ответ, и я могу принять это на благо других.
Я не совсем уверен, ожидает ли Spring именно этого. Однако добавление префикса ROLE_ к ролям в базе данных — это один из подходов, и, безусловно, доступны и другие методы. Лучше всего сравнить различные методы, чтобы найти оптимальный. Я добавлю это к своему предыдущему ответу. Я очень рад, что проблема решена. Хорошего дня - Кевин
Возможный дубликат stackoverflow.com/questions/41946473/…