Tabla de contenido
prefacio
Spring Boot 3 ha sido lanzado por un tiempo, y no hay muchos materiales sobre Spring Boot 3 en Internet . En línea con el entusiasmo por las nuevas tecnologías, he aprendido e investigado muchas funciones y características nuevas de Spring Boot 3. Los estudiantes interesados pueden consultar los datos oficiales de primavera para obtener una introducción detallada completa de nuevas características/mejoras
- Actualización de la versión Spring a 6.x
- Versión JDK al menos 17+
- …
Hay muchas funciones nuevas. Este artículo se centra principalmente en la integración de OAuth 2.0. Si desarrolla rápidamente sus propios servicios de autenticación y autorización, clientes de OAuth y servicios de recursos
Introducción al entorno de desarrollo de este artículo.
dependencias de desarrollo | Versión |
---|---|
Bota de primavera | 3.0.2 |
Descripción del puerto del entorno de desarrollo
Crear tres nuevos servicios, correspondientes al servicio de autenticación y autorización, cliente OAuth y servicio de recursos
Atender | puerto |
---|---|
Servicio de Autenticación y Autorización | 8080 |
Servicio de atención al cliente OAuth | 8081 |
servicio de recursos | 8082 |
Servicio de Autenticación y Autorización
dependencias pom.xml
Spring lanzó el proyecto spring-security-oauth2-authorization-server, la última versión es la versión 1.0, pom.xml depende de lo siguiente
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>${spring-security-oauth2-authorization-server.version}</version>
</dependency>
</dependencies>
Cree una nueva clase Oauth2ServerAutoConfiguration
package com.wen3.oauth.ss.authserver.authconfigure;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
@Configuration
public class Oauth2ServerAutoConfiguration {
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
http
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login"))
)
// Accept access tokens for User Info and/or Client Registration
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(new AntPathRequestMatcher("/actuator/**"),
new AntPathRequestMatcher("/oauth2/**"),
new AntPathRequestMatcher("/**/*.json"),
new AntPathRequestMatcher("/**/*.html")).permitAll()
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
// authorization server filter chain
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("test")
.password("test")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("demo-client-id")
.clientSecret("{noop}demo-client-secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
// .tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE).build())
.redirectUri("http://127.0.0.1:8081/login/oauth2/code/client-id-1")
.redirectUri("http://127.0.0.1:8081/login/oauth2/code/client-id-2")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope("message.read")
.scope("message.write")
.scope("user_info")
.scope("pull_requests")
// 登录成功后对scope进行确认授权
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}
}
función principal
@SpringBootApplication
public class OauthServerApplication {
public static void main(String[] args) {
SpringApplication.run(OauthServerApplication.class, args);
}
}
configuración yml
server:
port: 8080
Cliente OAuth de aplicaciones de terceros
dependencias pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
</dependencies>
Cree una nueva clase Oauth2ClientAutoConfiguration
package com.wen3.oauth.ss.authclient.autoconfigure;
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.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class Oauth2ClientAutoConfiguration {
@Bean
public SecurityFilterChain authorizationClientSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.anyRequest().authenticated()
.and().logout()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
.and().oauth2Client()
.and().oauth2Login();
return http.build();
}
}
Crear una nueva clase OauthClientDemoController
package com.wen3.oauth.ss.authclient.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class OauthClientDemoController {
@RequestMapping(path = "/hello")
public String demo() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
log.info("authentication: {}", authentication);
return "hello";
}
}
función principal
@SpringBootApplication
public class OauthServerApplication {
public static void main(String[] args) {
SpringApplication.run(OauthServerApplication.class, args);
}
}
configuración yml
server:
port: 8081
servlet:
session:
cookie:
# 需要更换存放sessionId的cookie名字,否则认证服务和客户端的sessionId会相互覆盖
name: JSESSIONID-2
spring:
security:
oauth2:
client:
registration:
client-id-1:
provider: demo-client-id
client-id: demo-client-id
client-secret: demo-client-secret
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'
# client-authentication-method: POST
scope: user_info, pull_requests
client-name: demo-client-id
client-id-2:
provider: demo-client-id2
client-id: demo-client-id
client-secret: demo-client-secret
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'
# client-authentication-method: POST
scope: user_info, pull_requests
client-name: demo-client-id2
provider:
demo-client-id:
authorization-uri: http://127.0.0.1:8080/oauth2/authorize
token-uri: http://127.0.0.1:8080/oauth2/token
user-info-uri: http://127.0.0.1:8082/user/info
user-name-attribute: name
jwk-set-uri: http://127.0.0.1:8080/oauth2/jwks
demo-client-id2:
authorization-uri: http://127.0.0.1:8080/oauth2/authorize
token-uri: http://127.0.0.1:8080/oauth2/token
user-info-uri: http://127.0.0.1:8082/user/info
user-name-attribute: name
jwk-set-uri: http://127.0.0.1:8080/oauth2/jwks
servicio de recursos
dependencias pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
</dependencies>
Cree una nueva clase ResourceServerAutoConfiguration
package com.wen3.oauth.ss.resourceserver.autoconfigure;
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.web.SecurityFilterChain;
@Configuration
public class ResourceServerAutoConfiguration {
@Bean
public SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().anyRequest().authenticated().and()
.oauth2ResourceServer().jwt();
return http.build();
}
}
Crear una nueva clase UserController
package com.wen3.oauth.ss.resourceserver.controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class UserController {
@RequestMapping(path = "/user/info", method = {
RequestMethod.GET,RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Map<String, Object> getUser(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> map = new HashMap<>();
map.put("name", "xxx");
return map;
}
}
función principal
package com.wen3.oauth.ss.resourceserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ResourceServerApplication {
public static void main(String[] args) {
SpringApplication.run(ResourceServerApplication.class, args);
}
}
configuración yml
server:
port: 8082
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://127.0.0.1:8080/oauth2/jwks
manifestación
- Ingrese en la barra de direcciones del navegador
http://127.0.0.1:8081/hello
Debido a que se configuran varios clientes, el usuario elegirá qué cliente usar para el inicio de sesión de OAuth
- Seleccione el primer cliente-id-1
- Saltar al servicio de autenticación y autorización para iniciar sesión
- Ingrese la prueba de nombre de usuario, la prueba de contraseña
- Saltar a la página de autorización después de un inicio de sesión exitoso
- Verifique el alcance para confirmar la autorización
- solicitud de redireccionamiento
Todas las páginas anteriores son las predeterminadas de Spring, el desarrollo comercial real personalizará estas páginas
Demostración de openid del cliente OAuth
- Escriba en el navegador
http://127.0.0.1:8080/.well-known/openid-configuration
{ "issuer":"http://127.0.0.1:8080","authorization_endpoint":"http://127.0.0.1:8080/oauth2/authorize","token_endpoint":"http://127.0.0.1:8080/oauth2/token","token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt"],"jwks_uri":"http://127.0.0.1:8080/oauth2/jwks","userinfo_endpoint":"http://127.0.0.1:8080/userinfo","response_types_supported":["code"],"grant_types_supported":["authorization_code","client_credentials","refresh_token"],"revocation_endpoint":"http://127.0.0.1:8080/oauth2/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt"],"introspection_endpoint":"http://127.0.0.1:8080/oauth2/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid"]}
- Si está configurado , se llamará para obtener la información de configuración
issuer-uri
al inicio , y si está configurado, se reemplazará con${issuer-uri}/.well-known/openid-configuration
provider
issuer-uri
path
/.well-known/openid-configuration
/.well-known/openid-configuration
La dirección obtenida de esta interfazuser-info-uri
eshttp://127.0.0.1:8080/userinfo
para que la información del usuario se obtenga del servicio de autorización.- Para permitir que
/userinfo
la interfaz del servicio de autorización regrese con normalidad, deberegistration
agregarla al alcance durante la configuraciónopenid
y, al mismo tiempo, debe agregar al menos uno deprofile
,email
,address
,phone
al alcance. es como sigue
server:
port: 8081
servlet:
session:
cookie:
# 需要更换存放sessionId的cookie名字,否则认证服务和客户端的sessionId会相互覆盖
name: JSESSIONID-2
spring:
security:
oauth2:
client:
registration:
client-id-1:
provider: demo-client-id
client-id: demo-client-id
client-secret: demo-client-secret
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'
# client-authentication-method: POST
scope: openid, profile, user_info, pull_requests
client-name: demo-client-id
client-id-2:
provider: demo-client-id2
client-id: demo-client-id
client-secret: demo-client-secret
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'
# client-authentication-method: POST
scope: openid, profile, user_info, pull_requests
client-name: demo-client-id2
provider:
demo-client-id:
issuer-uri: http://127.0.0.1:8080
demo-client-id2:
issuer-uri: http://127.0.0.1:8080
- La página de autorización cambiará
Finalizar
Después de estudiar el código fuente relevante de SpringBoot3, básicamente experimenté todas las funciones. Este artículo es principalmente para demostrar la última versión de la función OAuth integrada de SpringBoot. El principio detrás de esto, si tiene alguna pregunta, puede dejar un mensaje para comunicarse. .