Архитектура OAuth2/OpenID Connect в Java
1. Spring Security OAuth2 Client (рекомендуемый подход)
@Configuration
public class OAuth2LoginConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/login**", "/error**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.userInfoEndpoint(userInfo -> userInfo
.userService(customOAuth2UserService)
)
.successHandler(authenticationSuccessHandler)
)
.logout(logout -> logout
.logoutSuccessUrl("/")
.permitAll()
);
return http.build();
}
}
2. Кастомизация User Service
@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
OAuth2User oauth2User = delegate.loadUser(userRequest);
// Извлечение атрибутов из провайдера
Map<String, Object> attributes = oauth2User.getAttributes();
String registrationId = userRequest.getClientRegistration().getRegistrationId();
if ("google".equals(registrationId)) {
String email = (String) attributes.get("email");
String name = (String) attributes.get("name");
// Сохранение/обновление пользователя в БД
}
return new DefaultOAuth2User(
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")),
attributes,
"email" // nameAttributeKey
);
}
}
3. Конфигурация провайдеров
spring:
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_SECRET}
scope: openid, profile, email
github:
client-id: ${GITHUB_CLIENT_ID}
client-secret: ${GITHUB_SECRET}
scope: user:email, read:user
provider:
keycloak:
issuer-uri: ${KEYCLOAK_ISSUER_URI}
user-name-attribute: preferred_username
4. JWT-валидация для OpenID Connect
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri("https://idp.example.com/.well-known/jwks.json")
.build();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.decoder(jwtDecoder()))
);
return http.build();
}
5. Основные провайдеры и их особенности
| Провайдер |
Протокол |
Основные scope |
Особенности |
| Google |
OpenID Connect |
openid, profile, email |
Поддержка G Suite, проверка домена |
| GitHub |
OAuth2 |
user, repo |
Только OAuth2 (не OpenID Connect) |
| Microsoft |
OpenID Connect |
openid, profile, User.Read |
Интеграция с Azure AD |
| Keycloak |
OpenID Connect |
Настраиваемые |
Self-hosted, ролевая модель |
6. Безопасность и best practices
- PKCE (Proof Key for Code Exchange) для public clients
- State parameter для защиты от CSRF
- Хранение токенов: Access token в памяти, Refresh token в secure cookie
- Валидация issuer и audience в JWT
- Автоматическое обновление токенов через
OAuth2AuthorizedClientManager