Spring Authorization Server 如何构建高效授权中心?

2026-05-06 06:221阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1417个文字,预计阅读时间需要6分钟。

Spring Authorization Server 如何构建高效授权中心?

Spring Authorization Server 实现授权中心,源码地址:[链接]Spring Security 提供对 OAuth 2.0 框架的全面支持。Spring Authorization Server 的优势在于替代 Spring Security OAuth,支持 OAuth 2.1 授权框架。

Spring Authorization Server 实现授权中心

源码地址

当前,Spring Security 对 OAuth 2.0 框架提供了全面的支持。Spring Authorization Server 出现的含义在于替换 Spring Security OAuth,交付 OAuth 2.1 授权框架。 Spring 官方已弃用 Spring Security OAuth

本文涉及的组件版本如下:

组件 版本 JDK 17 org.springframework.boot 2.6.7 Gradle 7.4.1 spring-security-oauth2-authorization-server 0.2.3

spring-security-oauth2-authorization-server 项目由 Spring Security 团队领导,**社区驱动**。

本文的目的:

  1. 搭建授权中心示例
  2. fork 当前项目从而免去一些工作

本 demo 的结构

  • root
    • [[#auth-center|授权中心]]
    • [[#user-service|用户服务]]
    • [[#client-gateway|移动端网关]]

OAuth 2.1 支持三种许可类型,[[OAuth 2.1 授权框架#授权码许可]]、[[OAuth 2.1 授权框架#客户端证书许可]]、[[OAuth 2.1 授权框架#刷新令牌许可]]。

auth-center build.gradle

plugins { id 'org.springframework.boot' version '2.6.7' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.insight.into.life' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' implementation 'org.springframework.security:spring-security-oauth2-authorization-server:0.2.3' implementation 'org.springframework.boot:spring-boot-starter-actuator' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' // runtimeOnly 'mysql:mysql-connector-java' runtimeOnly "com.h2database:h2" annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' } tasks.named('test') { useJUnitPlatform() } config

... @EnableWebSecurity @Slf4j public class DefaultSecurityConfig { @Bean public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity 127.0.0.1:9100/login/oauth2/code/mobile-gateway-client-oidc") .redirectUri("127.0.0.1:9100/authorized") .scope(OidcScopes.OPENID) .scope("message.read") .scope("message.write") .build(); JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate); registeredClientRepository.save(registeredClient); return registeredClientRepository; } @Bean public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) { return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository); } @Bean public JWKSource<SecurityContext> jwkSource() { RSAKey rsaKey = Jwks.generateRsa(); JWKSet jwkSet = new JWKSet(rsaKey); return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); } @Bean public ProviderSettings providerSettings() { return ProviderSettings.builder().issuer("localhost:9000").build(); } @Bean public EmbeddedDatabase embeddedDatabase() { return new EmbeddedDatabaseBuilder() .generateUniqueName(true) .setType(EmbeddedDatabaseType.H2) .setScriptEncoding("UTF-8") .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql") .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") .build(); }

  1. 这里的两个 config 中有两个 SecurityFilterChain 类。调用顺序是 authorizationServerSecurityFilterChain、defaultSecurityFilterChain。
  2. registeredClientRepository 用于注册 client。这里的两个 redirectUri 中地址来自于[[#mobile-gateway|移动端网关]]。
application.yml

server: port: 9000 logging: level: root: INFO org.springframework.web: INFO org.springframework.security: INFO org.springframework.security.oauth2: INFO 启动服务

在浏览器中输入:localhost:9000/.well-known/openid-configuration,得到以下内容。

// 20220510135753 // localhost:9000/.well-known/openid-configuration { "issuer": "localhost:9000", "authorization_endpoint": "localhost:9000/oauth2/authorize", "token_endpoint": "localhost:9000/oauth2/token", "token_endpoint_auth_methods_supported": [ "client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt" ], "jwks_uri": "localhost:9000/oauth2/jwks", "userinfo_endpoint": "localhost:9000/userinfo", "response_types_supported": [ "code" ], "grant_types_supported": [ "authorization_code", "client_credentials", "refresh_token" ], "subject_types_supported": [ "public" ], "id_token_signing_alg_values_supported": [ "RS256" ], "scopes_supported": [ "openid" ] } user-service

用户服务在 demo 中的角色是资源服务器。

build.gradle

plugins { id 'org.springframework.boot' version '2.6.7' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.insight.into.life' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() } config

... @EnableWebSecurity public class ResourceServerConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity localhost:9000 启动服务

资源服务器目前不需要做额外配置,只需要启动即可。

client-gateway build.gradle

plugins { id 'org.springframework.boot' version '2.6.7' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.insight.into.life' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-web' implementation "org.springframework:spring-webflux" implementation "io.projectreactor.netty:reactor-netty" implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.2' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' annotationProcessor 'org.projectlombok:lombok' } tasks.named('test') { useJUnitPlatform() }

这里引入 org.springframework:spring-webfluxio.projectreactor.netty:reactor-netty 的原因在于使用了 WebClient。

config

... @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class LoopbackIpRedirectFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (request.getServerName().equals("localhost") && request.getHeader("host") != null) { UriComponents uri = UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request)) .host("127.0.0.1").build(); response.sendRedirect(uri.toUriString()); return; } filterChain.doFilter(request, response); } }

该配置用于转换地址。将 localhost 转换为 127.0.0.1

Spring Authorization Server 如何构建高效授权中心?

... @EnableWebSecurity @Slf4j public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity 127.0.0.1:9100/login/oauth2/code/{registrationId}" scope: openid client-gateway-authorization-code: provider: spring client-id: mobile-gateway-client client-secret: 123456 client-authentication-method: client_secret_basic authorization-grant-type: authorization_code redirect-uri: "127.0.0.1:9100/authorized" scope: message.read,message.write provider: spring: issuer-uri: localhost:9000 user-service: base-uri: 127.0.0.1:9001/menu/list 启动服务

在浏览器中输入:127.0.0.1:9100

输入账号密码:user1/password,这里的用户在 [[#auth-center#config]] 中配置。得到以下内容:

总结
  1. spring-authorization-server 目前还没有正式发布。文档较少。
  2. 还有一些需要完善的点。比如用户持久化、client 持久化。
  3. 此 demo 还要继续更新,为了能和本文对应,所以对应的 git tag 为 primitive-man

本文共计1417个文字,预计阅读时间需要6分钟。

Spring Authorization Server 如何构建高效授权中心?

Spring Authorization Server 实现授权中心,源码地址:[链接]Spring Security 提供对 OAuth 2.0 框架的全面支持。Spring Authorization Server 的优势在于替代 Spring Security OAuth,支持 OAuth 2.1 授权框架。

Spring Authorization Server 实现授权中心

源码地址

当前,Spring Security 对 OAuth 2.0 框架提供了全面的支持。Spring Authorization Server 出现的含义在于替换 Spring Security OAuth,交付 OAuth 2.1 授权框架。 Spring 官方已弃用 Spring Security OAuth

本文涉及的组件版本如下:

组件 版本 JDK 17 org.springframework.boot 2.6.7 Gradle 7.4.1 spring-security-oauth2-authorization-server 0.2.3

spring-security-oauth2-authorization-server 项目由 Spring Security 团队领导,**社区驱动**。

本文的目的:

  1. 搭建授权中心示例
  2. fork 当前项目从而免去一些工作

本 demo 的结构

  • root
    • [[#auth-center|授权中心]]
    • [[#user-service|用户服务]]
    • [[#client-gateway|移动端网关]]

OAuth 2.1 支持三种许可类型,[[OAuth 2.1 授权框架#授权码许可]]、[[OAuth 2.1 授权框架#客户端证书许可]]、[[OAuth 2.1 授权框架#刷新令牌许可]]。

auth-center build.gradle

plugins { id 'org.springframework.boot' version '2.6.7' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.insight.into.life' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' implementation 'org.springframework.security:spring-security-oauth2-authorization-server:0.2.3' implementation 'org.springframework.boot:spring-boot-starter-actuator' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' // runtimeOnly 'mysql:mysql-connector-java' runtimeOnly "com.h2database:h2" annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' } tasks.named('test') { useJUnitPlatform() } config

... @EnableWebSecurity @Slf4j public class DefaultSecurityConfig { @Bean public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity 127.0.0.1:9100/login/oauth2/code/mobile-gateway-client-oidc") .redirectUri("127.0.0.1:9100/authorized") .scope(OidcScopes.OPENID) .scope("message.read") .scope("message.write") .build(); JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate); registeredClientRepository.save(registeredClient); return registeredClientRepository; } @Bean public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) { return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository); } @Bean public JWKSource<SecurityContext> jwkSource() { RSAKey rsaKey = Jwks.generateRsa(); JWKSet jwkSet = new JWKSet(rsaKey); return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); } @Bean public ProviderSettings providerSettings() { return ProviderSettings.builder().issuer("localhost:9000").build(); } @Bean public EmbeddedDatabase embeddedDatabase() { return new EmbeddedDatabaseBuilder() .generateUniqueName(true) .setType(EmbeddedDatabaseType.H2) .setScriptEncoding("UTF-8") .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql") .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") .build(); }

  1. 这里的两个 config 中有两个 SecurityFilterChain 类。调用顺序是 authorizationServerSecurityFilterChain、defaultSecurityFilterChain。
  2. registeredClientRepository 用于注册 client。这里的两个 redirectUri 中地址来自于[[#mobile-gateway|移动端网关]]。
application.yml

server: port: 9000 logging: level: root: INFO org.springframework.web: INFO org.springframework.security: INFO org.springframework.security.oauth2: INFO 启动服务

在浏览器中输入:localhost:9000/.well-known/openid-configuration,得到以下内容。

// 20220510135753 // localhost:9000/.well-known/openid-configuration { "issuer": "localhost:9000", "authorization_endpoint": "localhost:9000/oauth2/authorize", "token_endpoint": "localhost:9000/oauth2/token", "token_endpoint_auth_methods_supported": [ "client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt" ], "jwks_uri": "localhost:9000/oauth2/jwks", "userinfo_endpoint": "localhost:9000/userinfo", "response_types_supported": [ "code" ], "grant_types_supported": [ "authorization_code", "client_credentials", "refresh_token" ], "subject_types_supported": [ "public" ], "id_token_signing_alg_values_supported": [ "RS256" ], "scopes_supported": [ "openid" ] } user-service

用户服务在 demo 中的角色是资源服务器。

build.gradle

plugins { id 'org.springframework.boot' version '2.6.7' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.insight.into.life' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() } config

... @EnableWebSecurity public class ResourceServerConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity localhost:9000 启动服务

资源服务器目前不需要做额外配置,只需要启动即可。

client-gateway build.gradle

plugins { id 'org.springframework.boot' version '2.6.7' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.insight.into.life' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-web' implementation "org.springframework:spring-webflux" implementation "io.projectreactor.netty:reactor-netty" implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.2' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' annotationProcessor 'org.projectlombok:lombok' } tasks.named('test') { useJUnitPlatform() }

这里引入 org.springframework:spring-webfluxio.projectreactor.netty:reactor-netty 的原因在于使用了 WebClient。

config

... @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class LoopbackIpRedirectFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (request.getServerName().equals("localhost") && request.getHeader("host") != null) { UriComponents uri = UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request)) .host("127.0.0.1").build(); response.sendRedirect(uri.toUriString()); return; } filterChain.doFilter(request, response); } }

该配置用于转换地址。将 localhost 转换为 127.0.0.1

Spring Authorization Server 如何构建高效授权中心?

... @EnableWebSecurity @Slf4j public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity 127.0.0.1:9100/login/oauth2/code/{registrationId}" scope: openid client-gateway-authorization-code: provider: spring client-id: mobile-gateway-client client-secret: 123456 client-authentication-method: client_secret_basic authorization-grant-type: authorization_code redirect-uri: "127.0.0.1:9100/authorized" scope: message.read,message.write provider: spring: issuer-uri: localhost:9000 user-service: base-uri: 127.0.0.1:9001/menu/list 启动服务

在浏览器中输入:127.0.0.1:9100

输入账号密码:user1/password,这里的用户在 [[#auth-center#config]] 中配置。得到以下内容:

总结
  1. spring-authorization-server 目前还没有正式发布。文档较少。
  2. 还有一些需要完善的点。比如用户持久化、client 持久化。
  3. 此 demo 还要继续更新,为了能和本文对应,所以对应的 git tag 为 primitive-man