Я создаю загрузочное приложение Spring с использованием oauth, прямо сейчас у меня работает вход в систему github, но я хочу иметь приложение на основе ролей, поэтому доступ к некоторым маршрутам может быть возможен только с помощью role_admin, а к другим - с помощью role_user, я Я пробовал кое-что, и мой код выглядит немного беспорядочным, поэтому я ценю любую помощь! Прямо сейчас в классе CustomOAuth2User в методе getAuthorities отображается Set roles = getUserRoles(); как пустое, поэтому по умолчанию для всех пользователей используется роль role_user, поэтому я могу получить доступ к маршрутам, которым нужна такая роль, но не к администраторским, даже если я создаю пользователя с ролью_admin.
Конфигурация безопасности
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private CustomOAuth2UserService oauthUserService;
@Autowired
private UserRepository userRepository;
@Autowired
private UserService userService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> {
auth.requestMatchers(HttpMethod.GET, "/userinfo").hasRole("ADMIN");
auth.requestMatchers(HttpMethod.GET, "/").hasRole("USER");
auth.anyRequest().authenticated();
})
.oauth2Login(oauth2 -> oauth2
.userInfoEndpoint(userInfo -> userInfo.userService(oauthUserService)).successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
CustomOAuth2User oauthUser = (CustomOAuth2User) authentication.getPrincipal();
String github_id = oauthUser.getAttributes().get("id").toString();
Optional<User> userExistOp = userRepository.findByIdentifier(github_id);
if (!userExistOp.isPresent()) {
//all user are saves with role User
UserDTO userDTO = new UserDTO(oauthUser.getName(),"username", oauthUser.getAttributes().get("id").toString(), oauthUser.getAttributes().get("avatar_url").toString(), Role.ADMIN);
userService.create(userDTO);
}
response.sendRedirect("/");
}
})
)
.build();
}
}
CustomOAuth2UserService
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User user = super.loadUser(userRequest);
return new CustomOAuth2User(user);
}
}
CustomOauth2User
public class CustomOAuth2User implements OAuth2User {
private OAuth2User oauth2User;
public CustomOAuth2User(OAuth2User oauth2User) {
this.oauth2User = oauth2User;
}
@Override
public Map<String, Object> getAttributes() {
return oauth2User.getAttributes();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<Role> roles = getUserRoles();
System.out.println("User roles:");
for (Role role : roles) {
System.out.println(role.name());
}
if (roles != null && !roles.isEmpty()) {
// Convert roles to GrantedAuthority objects
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.name()))
.collect(Collectors.toList());
} else {
// Provide a default role if user roles are not available
return Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));
}
}
@Override
public String getName() {
return oauth2User.getAttribute("name");
}
// Helper method to retrieve user roles from OAuth2User attributes
private Set<Role> getUserRoles() {
// Assuming roles are stored as an attribute with key "roles" in OAuth2User attributes
Collection<String> roleStrings = (Collection<String>) oauth2User.getAttribute("roles");
return roleStrings != null ?
roleStrings.stream()
.map(Role::valueOf) // Convert role string to Role enum
.collect(Collectors.toSet()) :
Collections.emptySet();
}
public String getEmail() {
return oauth2User.<String>getAttribute("email");
}
}
Модель пользователя
@Entity
@Table(name = "users")
@Data
@Setter
public class User implements UserDetails {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String name;
private String username;
private String identifier;
private String avatar;
private Role role;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if (this.role == Role.ADMIN) {
return List.of(new SimpleGrantedAuthority("ROLE_ADMIN"), new SimpleGrantedAuthority("ROLE_USER"));
}
return List.of(new SimpleGrantedAuthority("ROLE_USER"));
}
@Override
public String getPassword() {
return null;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
АутСервис
@Service
public class AuthService implements UserDetailsService{
UserRepository userRepository;
public AuthService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String identifier) throws UsernameNotFoundException {
Optional<User> userOp = userRepository.findByIdentifier(identifier);
if (!userOp.isPresent()) {
throw new UsernameNotFoundException("User not found with GitHub user ID: " + identifier);
}
User user = userOp.get();
// Construct UserDetails object with user roles
return org.springframework.security.core.userdetails.User
.withUsername(String.valueOf(user.getIdentifier())) // GitHub user ID
.password(user.getPassword())
.authorities(user.getAuthorities())
.accountExpired(true)
.accountLocked(true)
.credentialsExpired(true)
.disabled(true)
.build();
}
}
Pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ecommerce</groupId>
<artifactId>e-commerce</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>e-commerce</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>




Что вам следует сделать, так это использовать собственный сервер авторизации (Keycloak? Auth0? Amazon cognito? Вариантов много). Этот новый сервер авторизации будет отвечать за сохранение ролей и добавление их в частные заявки токенов.
Тогда все, что вам нужно настроить вручную в клиентах и серверах ресурсов Spring, — это сопоставление полномочий из частных утверждений, в которые ваш сервер авторизации помещает роли.
Если вы хотите понять OAuth2 в Spring Security, начните со спецификации OAuth2 , а затем продолжите Руководство по Spring Security . Чтобы быстро начать, вы можете просмотреть мои уроки, но чтобы по-настоящему понять, вам придется усвоить первые две ссылки.
Я прочитал немного вашего руководства, кажется, я совершаю ошибку, поскольку я создаю REST Api и использую oauth2-client для использования входа в систему oauth2. Я также прочитаю другой документ, на который вы ссылаетесь, этот сервер ресурсов и сервер авторизации все еще смущают меня, может быть, я буду использовать keycloak, чтобы не застрять в самой первой части проекта, чтобы я мог продолжить обучение больше вещей о весенней загрузке. Спасибо за помощь!
Keycloak — надежное и хорошо документированное решение, которым я пользуюсь каждый день. Не пропустите Основы OAuth2 во вступительном файле моих руководств, это должно помочь вам прояснить свои мысли об актерах OAuth2 и некоторых других (действительно) важных понятиях.
Это мне очень помогло, спасибо, что помогли мне найти лучшее решение моей проблемы. Я также прочитал в вашем блоге сообщение об oauth2 BFF, оно действительно интересное, хотя сейчас оно для меня слишком продвинутое, но я сохранил его, чтобы прочитать, когда получу больше знаний об oauth и keycloak. Еще раз спасибо за ваши ответы @ch4mp, они были очень полезны. Если у вас есть какие-либо другие ресурсы, которые, по вашему мнению, могут быть полезны, я буду признателен.
Я не шутил в своем первом комментарии. После того, как вы немного попрактиковались в моих руководствах и получили общее представление об OAuth2, вернитесь к спецификации OAuth2 и разделу OAuth2 руководства Spring Security, это единственный источник истины.
Спасибо за ваш ответ @ch4mp. Я подумывал сделать это вручную, чтобы понять, как все работает в Spring Security, поскольку я новичок в Spring Boot. Вы думаете, что в этом нет необходимости? стоит ли мне просто выбрать что-то вроде okta или keycloak?