diff --git a/gateway/.idea/workspace.xml b/gateway/.idea/workspace.xml index 0c4a32d..267654b 100644 --- a/gateway/.idea/workspace.xml +++ b/gateway/.idea/workspace.xml @@ -9,8 +9,9 @@ - - + + + @@ -115,7 +116,7 @@ - + diff --git a/gateway/src/main/java/com/tuoheng/gateway/config/SecurityConfig.java b/gateway/src/main/java/com/tuoheng/gateway/config/SecurityConfig.java index 8dc168f..b65652c 100644 --- a/gateway/src/main/java/com/tuoheng/gateway/config/SecurityConfig.java +++ b/gateway/src/main/java/com/tuoheng/gateway/config/SecurityConfig.java @@ -12,8 +12,8 @@ public class SecurityConfig { public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { http .authorizeExchange(exchanges -> exchanges - .pathMatchers("/test/**").permitAll() // 测试路径不需要认证 - .pathMatchers("/api/**").authenticated() + .pathMatchers("/a/**").authenticated() + .pathMatchers("/b/**").authenticated() .anyExchange().permitAll() ) .oauth2ResourceServer(oauth2 -> oauth2.jwt()); diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties index 62cdc6a..83f1639 100644 --- a/gateway/src/main/resources/application.properties +++ b/gateway/src/main/resources/application.properties @@ -1,14 +1,15 @@ server.port=8080 -spring.cloud.gateway.routes[0].id=resource-server +spring.cloud.gateway.routes[0].id=resource-server-a spring.cloud.gateway.routes[0].uri=http://localhost:8081 -spring.cloud.gateway.routes[0].predicates[0]=Path=/api/** -spring.cloud.gateway.routes[0].filters[0]=TokenRelay +spring.cloud.gateway.routes[0].predicates[0]=Path=/a/** +spring.cloud.gateway.routes[0].filters[0]=RewritePath=/a/(?.*), /api/${segment} +spring.cloud.gateway.routes[0].filters[1]=TokenRelay -# 添加一个不需要认证的测试路由 -spring.cloud.gateway.routes[1].id=test-route -spring.cloud.gateway.routes[1].uri=http://localhost:8081 -spring.cloud.gateway.routes[1].predicates[0]=Path=/test/** -spring.cloud.gateway.routes[1].filters[0]=RewritePath=/test/(?.*), /api/$\{segment} +spring.cloud.gateway.routes[1].id=resource-server-b +spring.cloud.gateway.routes[1].uri=http://localhost:8082 +spring.cloud.gateway.routes[1].predicates[0]=Path=/b/** +spring.cloud.gateway.routes[1].filters[0]=RewritePath=/b/(?.*), /api/${segment} +spring.cloud.gateway.routes[1].filters[1]=TokenRelay spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:9000/oauth2/jwks \ No newline at end of file diff --git a/gateway/target/classes/application.properties b/gateway/target/classes/application.properties deleted file mode 100644 index 62cdc6a..0000000 --- a/gateway/target/classes/application.properties +++ /dev/null @@ -1,14 +0,0 @@ -server.port=8080 - -spring.cloud.gateway.routes[0].id=resource-server -spring.cloud.gateway.routes[0].uri=http://localhost:8081 -spring.cloud.gateway.routes[0].predicates[0]=Path=/api/** -spring.cloud.gateway.routes[0].filters[0]=TokenRelay - -# 添加一个不需要认证的测试路由 -spring.cloud.gateway.routes[1].id=test-route -spring.cloud.gateway.routes[1].uri=http://localhost:8081 -spring.cloud.gateway.routes[1].predicates[0]=Path=/test/** -spring.cloud.gateway.routes[1].filters[0]=RewritePath=/test/(?.*), /api/$\{segment} - -spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:9000/oauth2/jwks \ No newline at end of file diff --git a/gateway/target/classes/com/tuoheng/gateway/GatewayApplication.class b/gateway/target/classes/com/tuoheng/gateway/GatewayApplication.class deleted file mode 100644 index 7bac476..0000000 Binary files a/gateway/target/classes/com/tuoheng/gateway/GatewayApplication.class and /dev/null differ diff --git a/gateway/target/classes/com/tuoheng/gateway/config/SecurityConfig.class b/gateway/target/classes/com/tuoheng/gateway/config/SecurityConfig.class deleted file mode 100644 index 6a0a8e2..0000000 Binary files a/gateway/target/classes/com/tuoheng/gateway/config/SecurityConfig.class and /dev/null differ diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 1c4d050..d85cab9 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -17,6 +17,11 @@ events { # HTTP模块配置 http { + map $http_origin $cors_origin { + default ""; + "https://a.local.com" "https://a.local.com"; + "https://b.local.com" "https://b.local.com"; + } # MIME类型 include /opt/homebrew/etc/nginx/mime.types; default_type application/octet-stream; @@ -51,7 +56,7 @@ http { # 前端应用服务器 (a.local.com) - HTTPS server { listen 443 ssl; - server_name a.local.com; + server_name b.local.com; # SSL配置 ssl_certificate /Users/sunpeng/workspace/remote/oauth2/ssl/certificate.crt; @@ -61,14 +66,14 @@ http { ssl_prefer_server_ciphers off; # 静态文件目录 - root /Users/sunpeng/workspace/remote/oauth2/resourceservicehtml; + root /Users/sunpeng/workspace/remote/oauth2/resourceservicehtmlb; # 首页和静态文件 location / { try_files $uri $uri/ /index.html; # 添加CORS头 - add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Origin $cors_origin; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; } @@ -86,6 +91,7 @@ http { # /api 路径转发到网关 location /api/ { + rewrite ^/api/(.*)$ /b/$1 break; proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; @@ -93,40 +99,14 @@ http { proxy_set_header X-Forwarded-Proto $scheme; # 处理CORS - add_header Access-Control-Allow-Origin "https://a.local.com" always; + add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Credentials "true" always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; # 处理OPTIONS请求 if ($request_method = 'OPTIONS') { - add_header Access-Control-Allow-Origin "https://a.local.com" always; - add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; - add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; - add_header Access-Control-Max-Age 1728000; - add_header Content-Type "text/plain; charset=utf-8"; - add_header Content-Length 0; - return 204; - } - } - - # /test 路径转发到网关 (测试用) - location /test/ { - proxy_pass http://localhost:8080; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # 处理CORS - add_header Access-Control-Allow-Origin "https://a.local.com" always; - add_header Access-Control-Allow-Credentials "true" always; - add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; - add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; - - # 处理OPTIONS请求 - if ($request_method = 'OPTIONS') { - add_header Access-Control-Allow-Origin "https://a.local.com" always; + add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; add_header Access-Control-Max-Age 1728000; @@ -149,14 +129,111 @@ http { access_log /tmp/nginx_oidc_logout.log main_cookie; # 处理CORS - add_header Access-Control-Allow-Origin "https://a.local.com" always; + add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Credentials "true" always; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; # 处理OPTIONS请求 if ($request_method = 'OPTIONS') { - add_header Access-Control-Allow-Origin "https://a.local.com" always; + add_header Access-Control-Allow-Origin $cors_origin always; + add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; + add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; + add_header Access-Control-Max-Age 1728000; + add_header Content-Type "text/plain; charset=utf-8"; + add_header Content-Length 0; + return 204; + } + } + + # 错误页面 + error_page 404 /404.html; + error_page 500 502 503 504 /50x.html; + } + + server { + listen 443 ssl; + server_name a.local.com; + + # SSL配置 + ssl_certificate /Users/sunpeng/workspace/remote/oauth2/ssl/certificate.crt; + ssl_certificate_key /Users/sunpeng/workspace/remote/oauth2/ssl/private.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + + # 静态文件目录 + root /Users/sunpeng/workspace/remote/oauth2/resourceservicehtml; + + # 首页和静态文件 + location / { + try_files $uri $uri/ /index.html; + + # 添加CORS头 + add_header Access-Control-Allow-Origin $cors_origin; + add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; + add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; + } + + # 防止前端server拦截OIDC登录页,/login直接返回404 + location = /login { + return 404; + } + + # 处理OPTIONS预检请求 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # /api 路径转发到网关 + location /api/ { + rewrite ^/api/(.*)$ /a/$1 break; + proxy_pass http://localhost:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # 处理CORS + add_header Access-Control-Allow-Origin $cors_origin always; + add_header Access-Control-Allow-Credentials "true" always; + add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; + add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; + + # 处理OPTIONS请求 + if ($request_method = 'OPTIONS') { + add_header Access-Control-Allow-Origin $cors_origin always; + add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; + add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; + add_header Access-Control-Max-Age 1728000; + add_header Content-Type "text/plain; charset=utf-8"; + add_header Content-Length 0; + return 204; + } + } + + # /oidc-logout 路径转发到 OIDC 服务 + location /oidc-logout { + proxy_pass http://localhost:9000/oidc-logout; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Cookie $http_cookie; + + # 记录cookie内容到专用日志 + access_log /tmp/nginx_oidc_logout.log main_cookie; + + # 处理CORS + add_header Access-Control-Allow-Origin $cors_origin always; + add_header Access-Control-Allow-Credentials "true" always; + add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; + add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; + + # 处理OPTIONS请求 + if ($request_method = 'OPTIONS') { + add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; add_header Access-Control-Max-Age 1728000; @@ -194,12 +271,12 @@ http { proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Ssl on; proxy_set_header Cookie $http_cookie; - add_header Access-Control-Allow-Origin "https://a.local.com" always; + add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Credentials "true" always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; if ($request_method = 'OPTIONS') { - add_header Access-Control-Allow-Origin "https://a.local.com" always; + add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; add_header Access-Control-Max-Age 1728000; diff --git a/oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java b/oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java index 3b37e4a..bee28d9 100644 --- a/oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java +++ b/oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java @@ -130,8 +130,24 @@ public class SecurityConfig { .build()) .build(); + // b.local 前端应用的 client + RegisteredClient bClient = RegisteredClient.withId(UUID.randomUUID().toString()) + .clientId("b-client") + .clientSecret(passwordEncoder.encode("b-secret")) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + .redirectUri("https://b.local.com/callback") + .scope(OidcScopes.OPENID) + .scope("read") + .clientSettings(ClientSettings.builder().requireAuthorizationConsent(false).build()) + .tokenSettings(TokenSettings.builder() + .accessTokenTimeToLive(Duration.ofMinutes(10)) + .refreshTokenTimeToLive(Duration.ofHours(1)) + .build()) + .build(); - return new InMemoryRegisteredClientRepository(aClient); + return new InMemoryRegisteredClientRepository(aClient, bClient); } // 生成 RSA 密钥对 diff --git a/resourceserviceb/.idea/.gitignore b/resourceserviceb/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/resourceserviceb/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/resourceserviceb/.idea/compiler.xml b/resourceserviceb/.idea/compiler.xml new file mode 100644 index 0000000..730d3e7 --- /dev/null +++ b/resourceserviceb/.idea/compiler.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resourceserviceb/.idea/encodings.xml b/resourceserviceb/.idea/encodings.xml new file mode 100644 index 0000000..63e9001 --- /dev/null +++ b/resourceserviceb/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/resourceserviceb/.idea/jarRepositories.xml b/resourceserviceb/.idea/jarRepositories.xml new file mode 100644 index 0000000..bef82d8 --- /dev/null +++ b/resourceserviceb/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resourceserviceb/.idea/misc.xml b/resourceserviceb/.idea/misc.xml new file mode 100644 index 0000000..5ddb3b3 --- /dev/null +++ b/resourceserviceb/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resourceserviceb/.idea/vcs.xml b/resourceserviceb/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/resourceserviceb/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/resourceserviceb/pom.xml b/resourceserviceb/pom.xml new file mode 100644 index 0000000..cd4ba5e --- /dev/null +++ b/resourceserviceb/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + + + com.tuoheng + resourceservice + 0.0.1-SNAPSHOT + resourceservice + Simple RESTful Resource Service + + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/resourceserviceb/src/main/java/com/tuoheng/resourceservice/HelloController.java b/resourceserviceb/src/main/java/com/tuoheng/resourceservice/HelloController.java new file mode 100644 index 0000000..93b5049 --- /dev/null +++ b/resourceserviceb/src/main/java/com/tuoheng/resourceservice/HelloController.java @@ -0,0 +1,18 @@ +package com.tuoheng.resourceservice; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import java.util.HashMap; +import java.util.Map; + +@RestController +public class HelloController { + @GetMapping("/api/hello") + public Map hello() { + Map response = new HashMap<>(); + response.put("message", "Hello from Resource Service!"); + response.put("timestamp", System.currentTimeMillis()); + response.put("service", "Resource Service"); + return response; + } +} \ No newline at end of file diff --git a/resourceserviceb/src/main/java/com/tuoheng/resourceservice/ResourceServiceApplication.java b/resourceserviceb/src/main/java/com/tuoheng/resourceservice/ResourceServiceApplication.java new file mode 100644 index 0000000..703a94d --- /dev/null +++ b/resourceserviceb/src/main/java/com/tuoheng/resourceservice/ResourceServiceApplication.java @@ -0,0 +1,11 @@ +package com.tuoheng.resourceservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ResourceServiceApplication { + public static void main(String[] args) { + SpringApplication.run(ResourceServiceApplication.class, args); + } +} \ No newline at end of file diff --git a/resourceserviceb/src/main/resources/application.properties b/resourceserviceb/src/main/resources/application.properties new file mode 100644 index 0000000..8d51d0c --- /dev/null +++ b/resourceserviceb/src/main/resources/application.properties @@ -0,0 +1 @@ +server.port=8082 \ No newline at end of file diff --git a/resourceservicehtmlb/callback.html b/resourceservicehtmlb/callback.html new file mode 100644 index 0000000..0612a41 --- /dev/null +++ b/resourceservicehtmlb/callback.html @@ -0,0 +1,204 @@ + + + + + OIDC回调处理 + + + +
+

处理OIDC回调

+ +
+
+

正在处理认证回调...

+
+ + +
+ + + + \ No newline at end of file diff --git a/resourceservicehtmlb/index.html b/resourceservicehtmlb/index.html new file mode 100644 index 0000000..da3fd42 --- /dev/null +++ b/resourceservicehtmlb/index.html @@ -0,0 +1,294 @@ + + + + + 系统B - OIDC登录 + + + +
+

系统B - OIDC登录

+ +
+ 正在检查登录状态... +
+ + + + + + +
+ + + + \ No newline at end of file