此版本仍在开发中,尚未被视为稳定版本。如需最新稳定版本,请使用 Spring Security 7.0.4spring-doc.cadn.net.cn

快速开始

如果你刚刚开始使用 Spring Security 授权服务器,以下部分将引导你创建你的第一个应用程序。spring-doc.cadn.net.cn

系统要求

Spring Security 授权服务器需要 Java 17 或更高版本的运行环境。spring-doc.cadn.net.cn

安装 Spring Security 授权服务器

开始使用 Spring Security 授权服务器最简单的方法是创建一个基于 Spring Boot 的应用程序。 你可以使用 start.spring.io 生成一个基础项目,或者参考 默认的授权服务器示例。 然后将 Spring Boot 提供的 Spring Security 授权服务器 Starter 添加为依赖项:spring-doc.cadn.net.cn

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>
implementation "org.springframework.boot:spring-boot-starter-oauth2-authorization-server"
有关在 Maven 或 Gradle 中使用 Spring Boot 的更多信息,请参阅安装 Spring Boot

或者,你可以不使用 Spring Boot,而是通过以下示例添加 Spring Security 授权服务器:spring-doc.cadn.net.cn

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
    <version>7.0.5-SNAPSHOT</version>
</dependency>
implementation "org.springframework.security:spring-security-oauth2-authorization-server:7.0.5-SNAPSHOT"

开发您的第一个应用程序

要开始使用,您需要将所需的最少组件定义为 @Bean。当使用 spring-boot-starter-oauth2-authorization-server 依赖项时,请定义以下属性,Spring Boot 将为您自动提供必要的 @Bean 定义:spring-doc.cadn.net.cn

application.yml
server:
  port: 9000

logging:
  level:
    org.springframework.security: trace

spring:
  security:
    user:
      name: user
      password: password
    oauth2:
      authorizationserver:
        client:
          oidc-client:
            registration:
              client-id: "oidc-client"
              client-secret: "{noop}secret"
              client-authentication-methods:
                - "client_secret_basic"
              authorization-grant-types:
                - "authorization_code"
                - "refresh_token"
              redirect-uris:
                - "http://127.0.0.1:8080/login/oauth2/code/oidc-client"
              post-logout-redirect-uris:
                - "http://127.0.0.1:8080/"
              scopes:
                - "openid"
                - "profile"
            require-authorization-consent: true

如果你想自定义默认的 HttpSecurity 配置,可以使用以下示例来覆盖 Spring Boot 的自动配置:spring-doc.cadn.net.cn

SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) {
		http
			.authorizeHttpRequests((authorize) ->
				authorize
					.anyRequest().authenticated()
			)
			.formLogin(Customizer.withDefaults())
			.oauth2AuthorizationServer((authorizationServer) ->
				authorizationServer
					.oidc(Customizer.withDefaults())	// Enable OpenID Connect 1.0
			);
		return http.build();
	}

}
除了入门体验之外,大多数用户都希望自定义默认配置。下一节演示如何自行提供所有必要的 Bean。

定义所需组件

如果你想自定义默认配置(无论是否使用 Spring Boot),可以在 Spring 的 @Bean 中将所需的最少组件定义为 @Configurationspring-doc.cadn.net.cn

这些组件可以按如下方式定义:spring-doc.cadn.net.cn

SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean (1)
	@Order(1)
	public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
			throws Exception {

		http
			.oauth2AuthorizationServer((authorizationServer) -> {
				http.securityMatcher(authorizationServer.getEndpointsMatcher());
				authorizationServer
					.oidc(Customizer.withDefaults());	// Enable OpenID Connect 1.0
			})
			.authorizeHttpRequests((authorize) ->
				authorize
					.anyRequest().authenticated()
			)
			// Redirect to the login page when not authenticated from the
			// authorization endpoint
			.exceptionHandling((exceptions) -> exceptions
				.defaultAuthenticationEntryPointFor(
					new LoginUrlAuthenticationEntryPoint("/login"),
					new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
				)
			);

		return http.build();
	}

	@Bean (2)
	@Order(2)
	public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
			throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			// Form login handles the redirect to the login page from the
			// authorization server filter chain
			.formLogin(Customizer.withDefaults());

		return http.build();
	}

	@Bean (3)
	public UserDetailsService userDetailsService() {
		UserDetails userDetails = User.withDefaultPasswordEncoder()
				.username("user")
				.password("password")
				.roles("USER")
				.build();

		return new InMemoryUserDetailsManager(userDetails);
	}

	@Bean (4)
	public RegisteredClientRepository registeredClientRepository() {
		RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
				.clientId("oidc-client")
				.clientSecret("{noop}secret")
				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
				.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
				.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
				.redirectUri("http://127.0.0.1:8080/login/oauth2/code/oidc-client")
				.postLogoutRedirectUri("http://127.0.0.1:8080/")
				.scope(OidcScopes.OPENID)
				.scope(OidcScopes.PROFILE)
				.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
				.build();

		return new InMemoryRegisteredClientRepository(oidcClient);
	}

	@Bean (5)
	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() { (6)
		KeyPair keyPair;
		try {
			KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
			keyPairGenerator.initialize(2048);
			keyPair = keyPairGenerator.generateKeyPair();
		}
		catch (Exception ex) {
			throw new IllegalStateException(ex);
		}
		return keyPair;
	}

	@Bean (7)
	public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
		return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
	}

	@Bean (8)
	public AuthorizationServerSettings authorizationServerSettings() {
		return AuthorizationServerSettings.builder().build();
	}

}

这是一个用于快速入门的最小化配置。要了解每个组件的用途,请参见以下说明:spring-doc.cadn.net.cn

1 用于协议端点的 Spring Security 过滤器链。
2 用于身份验证的 Spring Security 过滤器链。
3 用于检索用户进行身份验证的UserDetailsService实例。
4 用于管理客户端的 RegisteredClientRepository 实例。
5 用于对访问Tokens进行签名的 com.nimbusds.jose.jwk.source.JWKSource 实例。
6 一个在启动时生成密钥的 java.security.KeyPair 实例,用于创建上面的 JWKSource
7 用于解码签名访问Tokens的 JwtDecoder 实例。
8 用于配置 Spring Security 授权服务器的 AuthorizationServerSettings 实例。