目次
序文
Spring Boot 3 がリリースされてからしばらく経ちますが、インターネット上にはSpring Boot 3に関する資料があまりありません。新しいテクノロジーに対する熱意に合わせて、私はSpring Boot 3 の多くの新機能や特徴を学び、研究してきました。興味のある学生は、新機能/改善点の包括的な詳細な紹介について公式の春データを参照できます。
- 春のバージョン 6.x へのアップグレード
- JDK バージョン 17 以降
- …
多くの新機能があります。この記事では主に OAuth 2.0 の統合に焦点を当てています。独自の認証および認可サービス、OAuth クライアント、およびリソース サービスを迅速に開発する場合は、
この記事の開発環境の紹介
開発の依存関係 | バージョン |
---|---|
スプリングブーツ | 3.0.2 |
開発環境ポートの説明
認証および認可サービス、OAuth クライアント、およびリソース サービスに対応する 3 つの新しいサービスを作成します
仕える | ポート |
---|---|
認証・認可サービス | 8080 |
OAuthクライアントサービス | 8081 |
リソースサービス | 8082 |
認証・認可サービス
pom.xml の依存関係
Spring はspring-security-oauth2-authorization-server プロジェクトをリリースしました。最新バージョンはバージョン 1.0 です。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.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>${spring-security-oauth2-authorization-server.version}</version>
</dependency>
</dependencies>
新しい 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();
}
}
メイン機能
@SpringBootApplication
public class OauthServerApplication {
public static void main(String[] args) {
SpringApplication.run(OauthServerApplication.class, args);
}
}
yml設定
server:
port: 8080
サードパーティアプリケーションのOAuthクライアント
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>
新しい 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();
}
}
新しい 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";
}
}
メイン機能
@SpringBootApplication
public class OauthServerApplication {
public static void main(String[] args) {
SpringApplication.run(OauthServerApplication.class, args);
}
}
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
リソースサービス
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>
新しい 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();
}
}
新しい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;
}
}
メイン機能
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);
}
}
yml設定
server:
port: 8082
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://127.0.0.1:8080/oauth2/jwks
デモ
- ブラウザのアドレスバーに入力します
http://127.0.0.1:8081/hello
複数のクライアントが構成されているため、ユーザーは OAuth ログインに使用するクライアントを選択します。
- 最初の client-id-1 を選択します
- 認証・認可サービスにジャンプしてログインします
- ユーザー名テスト、パスワードテストを入力してください
- ログイン成功後、認証ページにジャンプします
- スコープをチェックして権限を確認します
- リダイレクトリクエスト
上記のページはすべて Spring のデフォルトです。実際のビジネス開発ではこれらのページをカスタマイズします。
OAuth クライアントの openid デモ
- ブラウザに入力してください
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"]}
- 構成されている場合は、
issuer-uri
起動時に構成情報を${issuer-uri}/.well-known/openid-configuration
取得するために呼び出されます。構成されている場合は、次のように置き換えられます。provider
issuer-uri
path
/.well-known/openid-configuration
- このインターフェースから
/.well-known/openid-configuration
取得したアドレスは、認可サービスからユーザー情報を取得するためのものuser-info-uri
です。http://127.0.0.1:8080/userinfo
- 認可サービスのインターフェースが正常に戻るようにするには、構成中にそれをスコープに追加する
/userinfo
必要があり、同時に、 、 、 の少なくとも 1 つをスコープに追加する必要があります。以下のとおりでありますregistration
openid
profile
email
address
phone
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
- 認証ページが変わります
終了
SpringBoot3 の関連ソース コードを学習した後、基本的にすべての機能を体験しました。この記事は主に SpringBoot 統合 OAuth 機能の最新バージョンをデモすることを目的としています。その背後にある原理について、ご質問がある場合は、メッセージを残して連絡してください。 。