Autentificarea unui client generic cu Spring Security OAuth2 Client
- 2 minute de citit
- 8 exemple de cod în Java și JSON
Disponibil și în English
Salut tuturor 👋,
Recent am lucrat la un mic proiect personal scris în Java cu Spring Framework și am avut dificultăți la autentificarea printr-un client OAuth2 extern folosind Spring Security. Soluția la problema mea a devenit clară după ce am parcurs codul și documentația pachetului Spring OAuth Client.
Credeam că va trebui să scriu clase personalizate ca să-l configurez pentru nevoia mea specifică, dar s-a dovedit că problema mea ținea strict de configurare.
Problema era că aveam un client OAuth2 Confidential, iar configurația implicită a Spring Security presupune că clientul este Public.
RFC-ul descrie tipurile de clienți după cum urmează:
Tipul de client Confidential este pentru aplicațiile care pot păstra în siguranță secretul clientului, de exemplu o aplicație web de back-end.Tipul de client Public este pentru aplicațiile care nu pot păstra în siguranță secretele clientului, de exemplu o aplicație mobilă sau o aplicație front-end. Utilizatorul poate oricând să parcurgă codul sursă și să obțină secretele.În proiectul meu Spring am configurat autentificarea în fișierul applications.properties. Observați că configurația clientului și a providerului se află sub cheia nuculabs.
# Configurarea clientului OAuth2spring.security.oauth2.client.registration.nuculabs.client-id=xxxspring.security.oauth2.client.registration.nuculabs.client-secret=xxxspring.security.oauth2.client.registration.nuculabs.scope=user:read,read:userspring.security.oauth2.client.registration.nuculabs.authorizationGrantType=authorization_codespring.security.oauth2.client.registration.nuculabs.redirectUri=http://localhost:8080/login/oauth2/code/nuculabsspring.security.oauth2.client.registration.nuculabs.client-authentication-method=client_secret_postspring.security.oauth2.client.provider.nuculabs.authorization-uri=https://www.example.com/oauth2/authorizespring.security.oauth2.client.provider.nuculabs.token-uri=https://www.example.com/api/oauth2/tokenspring.security.oauth2.client.provider.nuculabs.user-info-uri=https://www.example.com/api/mespring.security.oauth2.client.provider.nuculabs.user-name-attribute=meConfigurația pe care o ratam era client_secret_post.
Odată ce am terminat, a trebuit doar să adaug următoarea clasă SecurityConfig:
package dev.nuculabs.xenchat.xenchat.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.web.SecurityFilterChain;@Configuration@EnableWebSecuritypublic class SecurityConfig { // OAuth2AuthorizationCodeGrantRequestEntityConverter @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize .requestMatchers("/error", "/webjars/**", "/login").permitAll() .requestMatchers("/").authenticated() .anyRequest().authenticated() ) .oauth2Login(oauth2 -> oauth2 .loginPage("/login") .defaultSuccessUrl("/", true) ) .logout(logout -> logout .logoutSuccessUrl("/login") .permitAll() ); return http.build(); }}Și când vreau să autentific utilizatorii folosind clientul meu OAuth extern, trebuie doar să-i redirecționez către un link intern, ca să pornesc fluxul de autentificare implementat de Spring Security:
/oauth2/authorization/nuculabsUltimul pas este să configurez clientul OAuth extern cu următorul URL:
http://localhost:8080/login/oauth2/code/nuculabsAcesta încheie din punct de vedere tehnic fluxul de autentificare. Ce urmează este că Spring va încerca să preia datele utilizatorului de la user-info-uri; în cazul meu, API-ul este protejat prin access token și va răspunde cu un obiect “me” care arată astfel:
{ "me": { "about": "I’m Denis, a Software Engineer living in Romania. I’m passionate about cloud computing and software development ✨.", "activity_visible": false, "alert_optout": [], "allow_post_profile": "members", "allow_receive_news_feed": "members", "allow_send_personal_conversation": "members", "allow_view_identities": "members", "allow_view_profile": "members", "name": "Denis", ...}Ce am avut de făcut a fost să specific me ca user-name-attribute, astfel:
spring.security.oauth2.client.provider.nuculabs.user-name-attribute=meApoi pot accesa toate aceste câmpuri dintr-un controller, astfel:
package dev.nuculabs.xenchat.xenchat.controller;import org.springframework.security.core.annotation.AuthenticationPrincipal;import org.springframework.security.oauth2.core.user.OAuth2User;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.GetMapping;import java.util.LinkedHashMap;@Controllerpublic class HomeController {@GetMapping("/")public String home(@AuthenticationPrincipal OAuth2User principal, Model model) {LinkedHashMap<String, Object> userData = principal.getAttribute("me"); // Adaugă atributele utilizatorului în model model.addAttribute("name", userData.get("username")); model.addAttribute("email", userData.get("email")); model.addAttribute("title", userData.get("custom_title")); model.addAttribute("profileUrl", userData.get("view_url")); model.addAttribute("isModerator", userData.get("is_moderator")); // Adaugă toate atributele pentru depanare model.addAttribute("attributes", principal.getAttributes());return "home"; }@GetMapping("/login")public String login() {return "login"; }}Atât a fost tot, sper că acest articol v-a fost util! :D
Mulțumesc pentru lectură!