Используя Auth0 в качестве поставщика услуг OAuth, мы знаем, что роли могут влиять на пользователей (пока остаемся только на уровне Auth0). Однако эти роли не считаются напрямую предоставленными полномочиями при входе пользователя в систему. Я хотел иметь возможность получать роли пользователей с помощью API управления Auth0, чтобы получать роли пользователей и определять полномочия на основе ролей, которые будут предоставлены моему аутентифицированному пользователю. . Является ли это хорошей практикой в целом, и если да, то как мне действовать? Я пробовал некоторые конкретные реализации, но все еще сталкиваюсь с проблемой.
Я реализовал Customer OAuthUser, CustomOAuthUserService и добавил свой сервис в файл конфигурации безопасности Spring.
Вот мой пользовательский OAuthUser:
package com.interco.reconciliation.model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class CustomOAuth2User extends DefaultOAuth2User {
private final OAuth2User delegate;
private final Collection<GrantedAuthority> authorities;
public CustomOAuth2User(OAuth2User delegate, Collection<GrantedAuthority> authorities) {
super(authorities, delegate.getAttributes(), "name");
this.delegate = delegate;
this.authorities = authorities;
}
@Override
public Map<String, Object> getAttributes() {
return this.delegate.getAttributes();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getName() {
return this.delegate.getAttribute("name");
}
}
Вот моя пользовательская служба OAuth:
package com.interco.reconciliation.service;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import com.interco.reconciliation.model.CustomOAuth2User;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.exceptions.UnirestException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
@Autowired
private Auth0ApiManagementService auth0ApiManagementService;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
OAuth2User oAuth2User = super.loadUser(userRequest);
List<GrantedAuthority> authorities = new ArrayList<>(oAuth2User.getAuthorities());
String userId = oAuth2User.getAttribute("sub");
try {
HttpResponse<JsonNode> roles = this.auth0ApiManagementService.getUserRoles(userId);
JsonNode rolesBody = roles.getBody();
for(int i = 0; i < rolesBody.getArray().length(); i++) {
JSONObject role = rolesBody.getArray().getJSONObject(i);
String roleName = role.getString("name");
authorities.add(new SimpleGrantedAuthority("ROLE_" + roleName));
}
} catch (UnirestException | UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
System.out.println("User retrieved and authorities assigned");
return new CustomOAuth2User(oAuth2User, authorities);
}
}
Вот мой файл конфигурации безопасности:
package com.interco.reconciliation.config;
import static org.springframework.security.config.Customizer.withDefaults;
import com.interco.reconciliation.service.Auth0ApiManagementService;
import com.interco.reconciliation.service.CustomOAuth2UserService;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.exceptions.UnirestException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.io.IOException;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
@Value("${okta.oauth2.issuer}")
private String issuer;
@Value("${okta.oauth2.client-id}")
private String clientId;
@Autowired
private CustomOAuth2UserService customOAuth2UserService;
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable) // remove later or configure appropriately in production
.cors(withDefaults()) // remove later or configure appropriately in production
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2Login ->
oauth2Login
.userInfoEndpoint(userInfoEndpoint ->
userInfoEndpoint.userService(customOAuth2UserService)))
.logout(logout -> logout
.addLogoutHandler(logoutHandler()));
return http.build();
}
private LogoutHandler logoutHandler() {
return (request, response, authentication) -> {
try {
String baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
response.sendRedirect(issuer + "v2/logout?client_id = " + clientId + "&returnTo = " + baseUrl);
} catch (IOException e) {
throw new RuntimeException(e);
}
};
}
}
Вы настраиваете клиент Spring OAuth2 (с помощью oauth2Login
). Вам нужно:
issuer-uri
поставщика, конечная точка userinfo используется, когда issuer-uri
не установлен.GrantedAuthoritiesMapper
проще.Если вы настраивали oauth2ResourceServer
(REST API, авторизованный с использованием токенов на предъявителя) с декодером JWT, вам необходимо:
У меня есть дополнительный стартер Spring Boot, который, среди прочего, может:
GrantedAuthoritiesMapper
или Converter<Jwt, AbstractAuthenticationToken>
для себя.oauth2Login
:
audience
, необходимый для запроса авторизации.oauth2Login
основана на сеансах.