Недавно я перешел на Spring Boot 3, Java 17 и Spring Security 6.
У меня уже есть рабочий сервер авторизации, развернутый для тестовых и прод-сред, поэтому нет необходимости внедрять его с нуля внутри моего проекта. Осталось только правильно реализовать ресурсный сервер. До Spring Boot 3 я использовал OAuth 2, но недавно узнал, что OAuth2 полностью устарел и его разработка была перенесена в Spring Security 6.
Когда я запускал свой проект, поначалу я столкнулся со многими проблемами, поэтому мне пришлось удалить множество устаревших библиотек, функций и т. д.
У меня есть следующий исходный код для моего сервера ресурсов:
@Configuration
@EnableWebSecurity
public class ResourceServerConfiguration {
@Value("${spring.profiles.active}")
private String activeProfile;
@Value("${root.url.osb.basic.auth}")
private String osbBasicAuth;
@Value("${auth.server.url}")
private String oauthServerUrl;
@Value("${auth.server.clientId}")
private String clientId;
@Value("${auth.server.clientsecret}")
private String clientSecret;
public static final String BEARER_PREFIX = "Bearer ";
public static final String HEADER_NAME = "Authorization";
private final UserService userService;
public ResourceServerConfiguration(UserService userService) {
this.userService = userService;
}
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.addFilterAfter(new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
// We don't want to allow access to a resource with no token so clear
// the security context in case it is actually an OAuth2Authentication
var authHeader = request.getHeader(HEADER_NAME);
if (StringUtils.isEmpty(authHeader) || !StringUtils.startsWith(authHeader, BEARER_PREFIX)) {
SecurityContextHolder.clearContext();
}
filterChain.doFilter(request, response);
}
}, AbstractPreAuthenticatedProcessingFilter.class);
// http.csrf().disable();
http.authorizeHttpRequests(authorize -> authorize.requestMatchers("/", "/v1/open/**").permitAll());
http.authorizeHttpRequests(authorize -> authorize.requestMatchers("/pension/**").authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.opaqueToken(opaqueTokenConfigurer ->
opaqueTokenConfigurer.introspectionUri(oauthServerUrl)
.introspectionClientCredentials(clientId, clientSecret)));
if (StringUtils.equalsIgnoreCase(activeProfile, "dev")) {
http.authorizeHttpRequests(authorize ->
authorize.requestMatchers("/webjars/**", "/resources/**", "/swagger-ui.html", "/swagger-resources/**", "/v2/api-docs", "index.html")
.permitAll());
}
http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.opaqueToken(opaqueTokenConfigurer ->
opaqueTokenConfigurer.introspectionUri(oauthServerUrl)
.introspectionClientCredentials(clientId, clientSecret)));
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
String[] authParts = osbBasicAuth.split(":");
String username = authParts[0];
String password = authParts[1];
UserDetails userDetails = User.builder()
.username(username)
.password(new BCryptPasswordEncoder().encode(password)).roles()
.build();
return new InMemoryUserDetailsManager(userDetails);
}
}
Как только я получаю токен доступа с сервера авторизации и пытаюсь отправить запрос, выдается следующая ошибка:
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://blabla/dev/agent-auth/oauth/": PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at org.springframework.web.client.RestTemplate.createResourceAccessException(RestTemplate.java:915)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:895)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:740)
at org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector.makeRequest(SpringOpaqueTokenIntrospector.java:151)
... 75 common frames omitted
Раньше я решал эту проблему, когда использовал Spring boot 2 и Java 8, просто добавляя сертификаты в хранилище ключей, но даже в этот момент проблема не исчезла. В конце концов выяснилось, что проблема заключалась в том, что я добавлял сертификаты в хранилище ключей не в том месте. В этот момент я дважды проверил место добавления сертификатов, и когда я попытался отправить запрос, проблема все еще существует.
Местоположение JDK
Я добавляю все сертификаты в cacerts в этом месте.
Когда я использую .httpBasic(Customizer.withDefaults()) вместо oauth2ResourceServer и отправляю запросы, журналы ничего не показывают.
В Spring Boot 2 раньше существовали такие вещи, как RemoteTokenServices, которые, как я считаю, помогали автоматически решать проблемы при отправке запросов на сервер ресурсов с токенами доступа, но в Spring Boot 3, Spring Security 6 информации о них очень мало. правильные способы реализации аутентификации.
Такие руководства, как https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide
кажется, не дает много подробностей, и они неоднозначны для понимания.
Кроме того, во всех руководствах рассказывается, как реализовать аутентификацию с использованием JWT, но в этом руководстве я хочу реализовать ее с использованием простого токена Bearer, такого как аутентификация токена Opaque: https://docs.spring.io/spring-security/reference/servlet /oauth2/resource-server/opaque-token.html#oauth2resourceserver-opaque-introspectionuri-dsl
Вот почему, я прошу вашей помощи! Любая помощь будет оценена по достоинству. Спасибо!
После долгого времени решения этой проблемы мне удалось ее решить, заменив URL-адрес, к которому я обращаюсь, и экспортировав сертификат доверия этого URL-адреса в хранилище доверенных сертификатов.
Если исходный URL-адрес был https://blabla/dev/agent-auth/oauth/
, позже я заменил его на https://blabla/dev/agent-auth/oauth/check_token
, который является URL-адресом для проверки токена в моем проекте. Оказалось, что вся неразбериха заключалась в неправильном URL-адресе, который я использовал.
Кстати, мне удалось понять правильную реализацию конфигурации сервера ресурсов на Spring Security 6, которую я разместил по этой ссылке: OAuth2AuthenticatedPrincipal не загружается после выполнения интроспекции Я надеюсь, что эта ссылка окажется полезной для вашей собственной реализации.