我发表了一篇关于单页应用程序(UI)的文章,但在这篇文章中,我将介绍如何使用Spring框架和开源SSO框架Keycloak为J2EE应用程序构建微服务架构。这篇文章将涵盖以下几个方面:
Keycloak 设置
Eureka 服务注册和发现
Spring Cloud API 网关
Spring Security(OAuth2登录)以及与Keycloak的集成
微服务
该代码可在我的 Github 中找到,请先检查docker-compose.yml,以便您可以更轻松地阅读帖子的其余部分。我在这里需要提到的一件事是,在运行 docker 容器之前,您需要将 keycloak 服务器 URL 的 IP 地址替换为您自己的 IP 地址。
version: '3.4'
services:
api-gateway:
build:
context: ./api-gateway
ports:
- "8080:8080"
restart: on-failure
environment:
#overriding spring application.properties
- eureka.client.serviceUrl.defaultZone=http://eureka-server:9091/eureka/
- keycloak-client.server-url=http://10.0.0.17:18080/auth # use host name or ip of the host machine
depends_on:
- eureka-server
eureka-server:
build:
context: ./eureka-server
ports:
- "9091:9091"
restart: on-failure
microservice-consumer:
build:
context: ./microservice-consumer
ports:
- "9080:9080"
restart: on-failure
environment:
#overriding spring application.properties
- eureka.client.serviceUrl.defaultZone=http://eureka-server:9091/eureka/
- keycloak-client.server-url=http://10.0.0.17:18080/auth # use host name or ip of the host machine
depends_on:
- eureka-server
microservice-producer:
build:
context: ./microservice-producer
ports:
- "9081:9081"
restart: on-failure
environment:
#overriding spring application.properties
- eureka.client.serviceUrl.defaultZone=http://eureka-server:9091/eureka/
- keycloak-client.server-url=http://10.0.0.17:18080/auth # use host name or ip of the host machine
depends_on:
- eureka-server
keycloak:
image: jboss/keycloak:11.0.0
volumes:
- ./keycloak-server/realm-export.json:/tmp/keycloak/config/realm-export.json
environment:
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: admin
KEYCLOAK_IMPORT: /tmp/keycloak/config/realm-export.json
DB_VENDOR: POSTGRES
DB_ADDR: postgres
DB_DATABASE: keycloak
DB_USER: keycloak
DB_SCHEMA: public
DB_PASSWORD: password
ports:
- "18080:18080"
command:
- "-b"
- "0.0.0.0"
- "-Djboss.socket.binding.port-offset=10000"
restart: on-failure
depends_on:
- postgres
postgres:
image: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
volumes:
postgres_data:
name: keycloak_postgres_data
driver: local
然后,您可以运行以下 cmd 来启动 docker 容器。
docker-compose up --build
- 钥匙斗篷
Keycloak是一个开源的身份和访问管理解决方案。与 Spring Security OAuth2 框架集成非常方便。在这里,我将逐步展示如何设置密钥斗篷服务器。
首先,基于上面的 docker-compose 配置文件,请使用以下 cmd 构建并启动 keycloak 服务器。我导出了一个 JSON 文件(realm-export.json),该文件可供 keycloak docker 容器使用,因此您无需逐步配置。无论如何,我将一一展示该步骤。
docker-compose up --build keycloak
1.1 创建领域
使用 admin/admin 登录 http://localhost:18080/,然后创建一个如下领域,将所有字段保留为默认字段。
1.2 在上述领域下创建客户端
如上图所示,我们创建了三个客户端,分别代表 API 网关、微服务消费者和微服务生产者。有两件事需要提及。
首先,将必填字段(Valid Redirect URIs)填写为*,以便我们可以重定向回应用程序中配置的URI。
然后,选择客户端 ID 和 secrete 作为客户端身份验证器,这适用于与 Spring Security OAuth2 客户端集成。
1.3 创建用户
如下面的屏幕截图所示,我们需要填写一些必要的字段,例如名字、姓氏和电子邮件等。
2. Eureka Sever — 服务注册
基于 Spring Cloud Eureka 构建服务注册服务器非常简单,我们只需要注解@EnableEurekaServer即可实现。
- API 网关
Spring 框架 5 发布了 Spring Cloud 网关,以取代之前的网关 Spring Cloud Zuul。Spring Cloud Gateway 是建立在 Project Reactor、Spring WebFlux 和 Spring Boot 2.0 之上的新一代非阻塞网关,而 Zuul 是阻塞网关。 因此,在这篇文章中,我使用 Spring Cloud Gateway 作为微服务的 API 网关。
首先,我需要显示application.yml配置文件。
logging:
level:
root: WARN
org.springframework.web: INFO
org.springframework.security: DEBUG
org.springframework.security.oauth2: DEBUG
server:
keycloak-client:
server-url: http://localhost:18080/auth
realm: spring-micro-main
spring:
application:
name: api-gateway
security:
oauth2:
client:
registration:
keycloak:
provider: keycloak
client-id: spring-micro-gateway
client-secret: 756b0558-018b-4809-b478-bd5b4995d325
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login/oauth2/code/keycloak
scope: openid
provider:
keycloak:
authorization-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/auth
token-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/token
user-info-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/userinfo
jwk-set-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/certs
user-name-attribute: name
user-info-authentication-method: header
resourceserver:
jwt:
jwk-set-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/certs
cloud:
gateway:
routes:
- id: microservice-consumer
uri: lb://microservice-consumer
predicates:
- Path=/api/consume/**
filters:
- TokenRelay=
- RemoveRequestHeader=Cookie
- id: microservice-producer
uri: lb://microservice-producer
predicates:
- Path=/api/produce/**
filters:
- TokenRelay=
- RemoveRequestHeader=Cookie
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9091/eureka/
3.1 Spring Cloud Gateway中的路由定义
我们为两个微服务定义了两条路由:使用者和生产者。lb://microservice-consumer 意味着它指向基于服务注册服务器 Eureka 的微服务消费者。我们使用 TokenRelay 以便将请求中的access_token传递到微服务资源服务器,该服务器由 Spring Security OAuth2 和 Keycloak 保护。
3.2 Eureka 客户端配置
API 网关还充当 Eureka 客户端,以便它可以与微服务副本一起使用。我们只需要一个注释@EnableEurekaClient并在属性中配置 eureka 服务器 URL。
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9091/eureka/
3.3 Spring Security OAuth2 以及与 Keycloak 服务器的集成
3.3.1 Spring Security OAuth2
Spring Security 5 的主要功能之一是对 OAuth2 和 OIDC 的原生支持,而不是旧的 Spring Security OAuth 子项目中的旧客户端支持,与 IAM(身份和访问管理)提供商集成变得非常容易。我们需要添加以下依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
3.3.2 OAuth2 客户端注册
OAuth 2 客户端的属性以spring.security.oauth2.client.registration andspring.security.
oauth2.client.provider.特别是对于Keycloak,我们必须配置OAuth2提供程序的详细信息,并提供客户端注册的详细信息,如下所示。
spring:
security:
oauth2:
client:
registration:
keycloak:
provider: keycloak
client-id: spring-micro-consumer
client-secret: b2678444-3e56-466d-b035-a6109ca686ca
authorization-grant-type: authorization_code
redirect-uri: http://localhost:9080/login/oauth2/code/keycloak
scope: openid
provider:
keycloak:
authorization-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/auth
token-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/token
user-info-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/userinfo
jwk-set-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/certs
user-name-attribute: name
user-info-authentication-method: header
resourceserver:
jwt:
jwk-set-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/certs
3.3.3 Spring Security OAuth2 资源服务器
Keycloak使用公钥/私钥对来颁发和验证JWT(JSON Web Token)。具体而言,它使用私钥对访问令牌进行签名,而 OAuth2 客户端使用公钥来验证令牌。
我们需要将 API 网关作为 OAuth2 资源服务器进行保护,以便当请求在 Authorization 标头下带有持有者令牌时,它还可以验证令牌,而不是重定向到 SSO 服务器进行身份验证,然后请求最终可以转到下游微服务。例如,我们可以使用 HTTP 客户端发出请求,如下所示。
GET localhost:8080/api/produce/
Authorization: Bearer <your access token>
3.3.4 启用 OAuth2 登录和资源服务器
要启用 Spring Security OAuth2 登录以及资源服务器,我们需要利用 DSL 方法。
Spring Security 的 OAuth 2.0 登录支持是通过 Spring Security DSL 方法启用的。oauth2Login()
Spring Security 的 Resource Server 支持是通过 Spring Security DSL 方法启用的。oauth2ResourceServer
图示如下:
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SpringSecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
// @formatter:off
http
.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2Login()
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
// @formatter:on
}
}
- 微服务
微服务应受 Keycloak 保护,并作为 OAuth2 资源服务器启用。因此,配置类似于 API 网关。一个关键的区别是,Spring Cloud 网关是建立在 Spring WebFlux 之上的,而我们的微服务是建立在 Spring MVC 之上的。因此,安全配置略有不同,如下图所示。
@Configuration
public class OAuth2ResourceServerConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.and()
.oauth2ResourceServer()
.jwt();
}
}
就是这样,你就可以开始了。请检查我的 Github 中的源代码。