This commit is contained in:
parent
12a5550688
commit
e9208eb51b
|
|
@ -0,0 +1,190 @@
|
|||
# OAuth2/OIDC 统一认证系统
|
||||
|
||||
这是一个基于Spring Boot的多服务架构,使用OAuth2和OIDC实现统一认证。
|
||||
|
||||
## 系统架构
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ 前端应用 │ │ 网关服务 │ │ 资源服务 │
|
||||
│ (a.sun.com) │───▶│ (8080) │───▶│ (8081) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ OIDC服务器 │ │ JWT验证 │ │ 受保护资源 │
|
||||
│ (9000) │ │ Token转发 │ │ API端点 │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
## 服务组件
|
||||
|
||||
### 1. OIDC授权服务器 (oidc/)
|
||||
- **端口**: 9000
|
||||
- **功能**: OAuth2/OIDC授权服务器
|
||||
- **客户端配置**:
|
||||
- 客户端ID: `a-client`
|
||||
- 客户端密钥: `a-secret`
|
||||
- 重定向URI: `http://a.sun.com/callback`
|
||||
- 授权类型: `authorization_code`, `refresh_token`
|
||||
- 作用域: `openid`, `read`
|
||||
|
||||
### 2. 网关服务 (gateway/)
|
||||
- **端口**: 8080
|
||||
- **功能**: API网关,JWT验证,Token转发
|
||||
- **特性**:
|
||||
- JWT Token验证
|
||||
- 路由转发到后端服务
|
||||
- Token Relay功能
|
||||
|
||||
### 3. 资源服务 (resourceservice/)
|
||||
- **端口**: 8081
|
||||
- **功能**: 后端API服务
|
||||
- **端点**: `/api/resource` (GET)
|
||||
|
||||
### 4. 前端应用 (resourceservicehtml/)
|
||||
- **域名**: a.sun.com
|
||||
- **功能**: 用户界面,OIDC登录流程
|
||||
- **页面**:
|
||||
- `index.html`: 主页面,包含登录和API调用功能
|
||||
- `callback.html`: OIDC回调处理页面
|
||||
- `test.html`: 配置测试页面
|
||||
|
||||
## 认证流程
|
||||
|
||||
### 1. 用户登录流程
|
||||
1. 用户访问 `http://a.sun.com`
|
||||
2. 前端检查本地Token
|
||||
3. 如果没有Token,重定向到OIDC登录页面
|
||||
4. 用户在OIDC服务器登录
|
||||
5. OIDC服务器重定向回 `http://a.sun.com/callback`
|
||||
6. 前端用授权码交换Token
|
||||
7. Token保存到本地存储
|
||||
|
||||
### 2. API调用流程
|
||||
1. 前端使用Token调用 `/api/resource`
|
||||
2. 请求经过nginx代理到网关
|
||||
3. 网关验证JWT Token
|
||||
4. 网关转发请求到资源服务
|
||||
5. 资源服务返回数据
|
||||
|
||||
## 配置说明
|
||||
|
||||
### OIDC服务器配置
|
||||
- 使用内存存储用户和客户端
|
||||
- 默认用户: `user/password`
|
||||
- RSA密钥对用于JWT签名
|
||||
|
||||
### 网关配置
|
||||
- WebFlux架构
|
||||
- JWT Token验证
|
||||
- 路由配置指向资源服务
|
||||
|
||||
### 前端配置
|
||||
- OIDC客户端配置
|
||||
- Token本地存储
|
||||
- 状态参数防CSRF攻击
|
||||
|
||||
## 启动顺序
|
||||
|
||||
1. **启动OIDC服务器**:
|
||||
```bash
|
||||
cd oidc
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
2. **启动网关服务**:
|
||||
```bash
|
||||
cd gateway
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
3. **启动资源服务**:
|
||||
```bash
|
||||
cd resourceservice
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
4. **配置nginx**:
|
||||
```bash
|
||||
sudo cp resourceservicehtml/nginx.conf /opt/homebrew/etc/nginx/nginx.conf
|
||||
sudo nginx -s reload
|
||||
```
|
||||
|
||||
5. **配置hosts**:
|
||||
```
|
||||
127.0.0.1 a.sun.com
|
||||
127.0.0.1 oidc.sun.com
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
### 1. 访问前端
|
||||
- 主页: `http://a.sun.com`
|
||||
- 测试页面: `http://a.sun.com/test.html`
|
||||
|
||||
### 2. 测试OIDC端点
|
||||
- JWKS: `http://localhost:9000/oauth2/jwks`
|
||||
- 授权端点: `http://localhost:9000/oauth2/authorize`
|
||||
- Token端点: `http://localhost:9000/oauth2/token`
|
||||
|
||||
### 3. 测试API
|
||||
- 资源API: `http://a.sun.com/api/resource`
|
||||
|
||||
## 安全特性
|
||||
|
||||
- JWT Token验证
|
||||
- 状态参数防CSRF攻击
|
||||
- HTTPS重定向支持
|
||||
- Token过期处理
|
||||
- 刷新Token机制
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **OIDC发现端点无法访问**
|
||||
- 检查OIDC服务器是否启动
|
||||
- 验证安全配置
|
||||
|
||||
2. **Token验证失败**
|
||||
- 检查Token是否过期
|
||||
- 验证JWT签名
|
||||
|
||||
3. **nginx权限问题**
|
||||
- 确保nginx用户有读取权限
|
||||
- 检查目录权限设置
|
||||
|
||||
4. **CORS问题**
|
||||
- 检查前端域名配置
|
||||
- 验证重定向URI设置
|
||||
|
||||
## 开发说明
|
||||
|
||||
### 添加新客户端
|
||||
在 `oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java` 中添加新的 `RegisteredClient`。
|
||||
|
||||
### 添加新API端点
|
||||
在 `resourceservice` 中添加新的Controller和端点。
|
||||
|
||||
### 修改前端配置
|
||||
在 `resourceservicehtml/index.html` 中修改 `oidcConfig` 对象。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **后端**: Spring Boot 3.x, Spring Security, Spring Cloud Gateway
|
||||
- **前端**: HTML5, JavaScript, CSS3
|
||||
- **认证**: OAuth2, OpenID Connect, JWT
|
||||
- **代理**: nginx
|
||||
- **构建**: Maven
|
||||
|
||||
## 版本信息
|
||||
|
||||
- Spring Boot: 3.5.3
|
||||
- Spring Security: 6.5.1
|
||||
- Spring Cloud Gateway: 4.1.1
|
||||
- Java: 17
|
||||
|
||||
|
||||
mkcert -uninstall && mkcert -install && mkcert -key-file ssl/private.key -cert-file ssl/certificate.crt "*.local.com" local.com
|
||||
|
|
@ -5,7 +5,17 @@
|
|||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="b713637a-3b19-4c5b-9e88-95e35dd83d2e" name="更改" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/../nginx/nginx.conf" beforeDir="false" afterPath="$PROJECT_DIR$/../nginx/nginx.conf" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../oidc/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../oidc/pom.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java" beforeDir="false" afterPath="$PROJECT_DIR$/../oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../oidc/src/main/resources/application.properties" beforeDir="false" afterPath="$PROJECT_DIR$/../oidc/src/main/resources/application.properties" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../resourceservice/target/classes/com/tuoheng/resourceservice/HelloController.class" beforeDir="false" afterPath="$PROJECT_DIR$/../resourceservice/target/classes/com/tuoheng/resourceservice/HelloController.class" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../resourceservice/target/classes/com/tuoheng/resourceservice/ResourceServiceApplication.class" beforeDir="false" afterPath="$PROJECT_DIR$/../resourceservice/target/classes/com/tuoheng/resourceservice/ResourceServiceApplication.class" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../resourceservicehtml/callback.html" beforeDir="false" afterPath="$PROJECT_DIR$/../resourceservicehtml/callback.html" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../resourceservicehtml/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/../resourceservicehtml/index.html" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../resourceservicehtml/test.html" beforeDir="false" afterPath="$PROJECT_DIR$/../resourceservicehtml/test.html" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../ssl/certificate.crt" beforeDir="false" afterPath="$PROJECT_DIR$/../ssl/certificate.crt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../ssl/private.key" beforeDir="false" afterPath="$PROJECT_DIR$/../ssl/private.key" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
|
|
@ -105,7 +115,7 @@
|
|||
<workItem from="1752741336600" duration="2274000" />
|
||||
<workItem from="1752745264222" duration="2832000" />
|
||||
<workItem from="1752751160098" duration="1320000" />
|
||||
<workItem from="1752798688493" duration="5384000" />
|
||||
<workItem from="1752798688493" duration="8093000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>OAuth2 登录</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; background: #f5f5f5; }
|
||||
.login-box { width: 300px; margin: 100px auto; background: #fff; padding: 30px; border-radius: 8px; box-shadow: 0 2px 8px #ccc; }
|
||||
.login-box h2 { text-align: center; margin-bottom: 20px; }
|
||||
.login-box input { width: 100%; padding: 8px; margin: 10px 0; border: 1px solid #ddd; border-radius: 4px; }
|
||||
.login-box button { width: 100%; padding: 10px; background: #1976d2; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
|
||||
.login-box button:disabled { background: #aaa; }
|
||||
.msg { color: #d32f2f; text-align: center; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-box">
|
||||
<h2>OAuth2 登录</h2>
|
||||
<form method="post" action="/login">
|
||||
<input type="text" name="username" placeholder="用户名" required autocomplete="username">
|
||||
<input type="password" name="password" placeholder="密码" required autocomplete="current-password">
|
||||
<button type="submit">登录</button>
|
||||
</form>
|
||||
<div class="msg"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/FXUfK+ry8hUJ
|
||||
H3mYhlDukXW05kLTRjyMd8G7s8Qt3M9B1e92rxHNKcU45+lSDTF8bb1JF6DzCXmo
|
||||
Clon/6H8YvHzD3RyypmqCRi17qQwch5Jtprtb8lCJVcBClDUFjoeTtPXpJMXdH5P
|
||||
xjLR6Aa3Z4HpBpIKMsvp9ZaAG0XQDTUo8urcIeV16iRsRfqLRufwQ4gVlVxuOJVb
|
||||
mHmKyBJZja+iBVlsiVBggMQJq2a2HQE4Lk2lv+7uWqbJ2MrL8d8aOkJIt+YtqlxV
|
||||
mv2vLoLw2hAd4emnF2ijFEYJtNzepJ5X0nltzAHQd+uW44sTjWlMOopqKpqD1+R9
|
||||
ZNsMsdupAgMBAAECggEAGVETenTMJTITvWixKJcrI+Cb0sLrOajFnurC/UZ9CIKH
|
||||
5zYcCwJ4/lC5c6euTxO2acD0YjnCNlEcEDqG5WPGJ3VIjyaODCNxpoicAIbEtDJ6
|
||||
dtO9xRWZea0O0PF38hGb06YoBRsl7eaeUZ114D+4nBYXrTMUqEtAnxfNv91dK4pJ
|
||||
byV1Wp+bHgXzIH4kM3dPjOsDwAUIgtmUFrSXdsG+OzlTZxUiCShAWFXe9W5KiLR0
|
||||
fZJkCu2qhLg7QQvgrGmp4PRov97dJqbh0XXCyDLHmVbjqrkCD8AcdBqtRI45B2TA
|
||||
STbNcq6nlXf2VOsVjHnDoIEDwpDDeCDesDfnSVPzgQKBgQDE75cWIjH7/DXBJ0rN
|
||||
x81v2MwEZw3zLdq5GNENPitbHRgPZ8jvEsABg5hJButucjyxOPopdYQg+c6QhkMG
|
||||
qumig3k9aOVonVOrF3+iXGoMWjp3yZypS15hJzx1M7WneY1ctE3RQ2tIwOk8xKPB
|
||||
BXSOsbAlVIlUfRqHO9h0m2JTeQKBgQD4ZI3oXAkKAYfGlZRY+FJ+As/p9PhQovv6
|
||||
nq/F1BwF9NvLjihzt71pYVbEcZkkUA2QHxQEMKerVLpRIhaschmnCIyqh8w1wfyj
|
||||
QfO5fi3e3amtkXyRU1CnXPh4ywtWmpvZtmoDqol13Zjjzjose2GGt4hpZsBggVHQ
|
||||
6jPgrAENsQKBgHMYi7aV2ZyptEjky+UkZr59eA8Co7aCEBipllQlB3XCtTMbtuVy
|
||||
keDQpgnYD3SHM01oPVxJoCUdmkoBDd8xuEYQjKUFTz4q5KFTpHahiCEcApvLqtGO
|
||||
iORC6CSfSgVNFv8dKXWp72OfyzCGxCWlKI/U7VuD4pcMXpq2sTTFM1wRAoGBANZW
|
||||
rr2a7ZHc0DTkTiaX4VcrRg40fTHX8mfJFxQ2fBgHusJj4TQ5kRCmFiFdhTB4g7uh
|
||||
lbwn4AdQDZaFO9uCefBQyFE+7VBWHJMkDhQ6dYqi7BACQuOEaUyCRUa2rwoEUAgG
|
||||
CGUxe3xhw9SP2FMaBIYjSWrqZ4bfEKKd9jYhNqeBAoGBAK9adFsDFQtT2O4u25hx
|
||||
Yv7m9ZG5NbvhuQuEK7skdDb+HZHK/1JIV8WG16zTe897I7IC+OYezKLA1fbBnna0
|
||||
rARwY/NZL1/CA5SmPGgK5Vy9IJbyJ50tQqFUGaF0SF/5jAuzCLa82d3XWUOlUgWH
|
||||
zkITyIa3HhT//7eFdH3sa+Hc
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEWTCCAsGgAwIBAgIQBKpJXSm53rsT3EorgGHTXjANBgkqhkiG9w0BAQsFADCB
|
||||
kzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTQwMgYDVQQLDCtzdW5w
|
||||
ZW5nQHN1bnBlbmdkZU1hY0Jvb2stUHJvLmxvY2FsICjlrZnpuY8pMTswOQYDVQQD
|
||||
DDJta2NlcnQgc3VucGVuZ0BzdW5wZW5nZGVNYWNCb29rLVByby5sb2NhbCAo5a2Z
|
||||
6bmPKTAeFw0yNTA3MTgwNzU0NTRaFw0yNzEwMTgwNzU0NTRaMF8xJzAlBgNVBAoT
|
||||
Hm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTE0MDIGA1UECwwrc3VucGVu
|
||||
Z0BzdW5wZW5nZGVNYWNCb29rLVByby5sb2NhbCAo5a2Z6bmPKTCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAL8VdR8r6vLyFQkfeZiGUO6RdbTmQtNGPIx3
|
||||
wbuzxC3cz0HV73avEc0pxTjn6VINMXxtvUkXoPMJeagKWif/ofxi8fMPdHLKmaoJ
|
||||
GLXupDByHkm2mu1vyUIlVwEKUNQWOh5O09ekkxd0fk/GMtHoBrdngekGkgoyy+n1
|
||||
loAbRdANNSjy6twh5XXqJGxF+otG5/BDiBWVXG44lVuYeYrIElmNr6IFWWyJUGCA
|
||||
xAmrZrYdATguTaW/7u5apsnYysvx3xo6Qki35i2qXFWa/a8ugvDaEB3h6acXaKMU
|
||||
Rgm03N6knlfSeW3MAdB365bjixONaUw6imoqmoPX5H1k2wyx26kCAwEAAaNcMFow
|
||||
DgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaA
|
||||
FF75UQ00SANrn+spMSH1elRO+PN9MBIGA1UdEQQLMAmCB2luc3RhbGwwDQYJKoZI
|
||||
hvcNAQELBQADggGBAF7D7B5a2ZZ4vSgqGx8e0p08dVfUHM3v85pHi7VO7xVaHKWh
|
||||
rS09S5sTVvDiEOujHXlnrGYBQjOZ3/DwBJyNQQG83EWZ1/TWyYIrmBUUBzpaAJ+t
|
||||
gLap5KaQoV+xo46PfXCG5bQmqllcsZKWvY5c8chECHXWJMX7cF8/9XBI7gywIoRN
|
||||
PRVKoXjh5SAoOzI8VjnQ3gEXywnuBfaSu/76oNT9ty/Y94G6RhYLq+s0Hpj5FAi2
|
||||
KbznwKAk14PfUDuZenPKac1NFNdlRnjaBoIQBtikSQjM9TaBMD1xvMhmxO779/0P
|
||||
kBrtzJcYez/SOrTFGUWMaM9CQSqBrINOJah56QB0eP1o1NvnzV7L/2c0kDQVJMMM
|
||||
oy+1Qfx9OhYvAv/xmORWNlGw+oF4bh71qqlWcQ3mqrYBsYIfxt/1MsYawtg3waer
|
||||
h/CO5vPOlLplqW8H7hrU8e4zq9QRMSLlMNycYQs+S8JTlL42lT6a4dXDXBwx4NlP
|
||||
QeQTR9ak1+yacEzE2w==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -26,6 +26,12 @@ http {
|
|||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
# 在http块添加main_cookie日志格式
|
||||
log_format main_cookie '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||
'cookie:"$http_cookie"';
|
||||
|
||||
# 访问日志
|
||||
access_log /tmp/nginx_access.log main;
|
||||
|
||||
|
|
@ -42,10 +48,10 @@ http {
|
|||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
|
||||
|
||||
# 前端应用服务器 (a.com) - HTTPS
|
||||
# 前端应用服务器 (a.local.com) - HTTPS
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name a.com;
|
||||
server_name a.local.com;
|
||||
|
||||
# SSL配置
|
||||
ssl_certificate /Users/sunpeng/workspace/remote/oauth2/ssl/certificate.crt;
|
||||
|
|
@ -67,6 +73,11 @@ http {
|
|||
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;
|
||||
|
|
@ -82,14 +93,14 @@ http {
|
|||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 处理CORS
|
||||
add_header Access-Control-Allow-Origin "https://a.com" always;
|
||||
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.com" always;
|
||||
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;
|
||||
|
|
@ -108,14 +119,14 @@ http {
|
|||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 处理CORS
|
||||
add_header Access-Control-Allow-Origin "https://a.com" always;
|
||||
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.com" always;
|
||||
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;
|
||||
|
|
@ -125,15 +136,45 @@ http {
|
|||
}
|
||||
}
|
||||
|
||||
# /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 "https://a.local.com" 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-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;
|
||||
}
|
||||
|
||||
# OIDC服务器代理 (oidc.com) - HTTPS
|
||||
# OIDC服务器代理 (oidc.local.com) - HTTPS
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name oidc.com;
|
||||
server_name oidc.local.com;
|
||||
|
||||
# SSL配置
|
||||
ssl_certificate /Users/sunpeng/workspace/remote/oauth2/ssl/certificate.crt;
|
||||
|
|
@ -144,25 +185,21 @@ http {
|
|||
|
||||
# 代理到OIDC服务器
|
||||
location / {
|
||||
proxy_pass http://localhost:9000;
|
||||
proxy_pass http://localhost:9000/;
|
||||
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 X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Ssl on;
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
|
||||
# 处理CORS
|
||||
add_header Access-Control-Allow-Origin "https://a.com" always;
|
||||
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";
|
||||
|
||||
# 移除CSP限制 (开发环境)
|
||||
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: http: https:;" always;
|
||||
|
||||
# 处理OPTIONS请求
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header Access-Control-Allow-Origin "https://a.com" always;
|
||||
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;
|
||||
|
|
@ -180,7 +217,7 @@ http {
|
|||
# HTTP重定向到HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name a.com oidc.com;
|
||||
server_name a.local.com oidc.local.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ public class SecurityConfig {
|
|||
.requestMatchers("/oauth2/jwks").permitAll()
|
||||
.requestMatchers("/logout").permitAll()
|
||||
.requestMatchers("/login").permitAll()
|
||||
.requestMatchers("/oidc-logout").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.oauth2ResourceServer(oauth2 -> oauth2.jwt()) // 新增,支持JWT
|
||||
|
|
@ -112,14 +113,14 @@ public class SecurityConfig {
|
|||
// 注册客户端(内存存储)
|
||||
@Bean
|
||||
public RegisteredClientRepository registeredClientRepository(PasswordEncoder passwordEncoder) {
|
||||
// a.com 前端应用的 client
|
||||
// a.sun.com 前端应用的 client
|
||||
RegisteredClient aClient = RegisteredClient.withId(UUID.randomUUID().toString())
|
||||
.clientId("a-client")
|
||||
.clientSecret(passwordEncoder.encode("a-secret"))
|
||||
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
|
||||
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
|
||||
.redirectUri("https://a.com/callback")
|
||||
.redirectUri("https://a.local.com/callback")
|
||||
.scope(OidcScopes.OPENID)
|
||||
.scope("read")
|
||||
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(false).build())
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tuoheng.oauth.oidc.controller;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Controller
|
||||
public class OidcLogoutController {
|
||||
|
||||
@GetMapping("/oidc-logout")
|
||||
public String oidcLogout(
|
||||
@RequestParam(value = "id_token_hint", required = false) String idTokenHint,
|
||||
@RequestParam(value = "post_logout_redirect_uri", required = false) String redirectUri,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Authentication authentication) {
|
||||
|
||||
// 调用Spring Security的logout
|
||||
new SecurityContextLogoutHandler().logout(request, response, authentication);
|
||||
|
||||
// 重定向到指定页面
|
||||
if (redirectUri != null && !redirectUri.isEmpty()) {
|
||||
return "redirect:" + redirectUri;
|
||||
}
|
||||
return "redirect:/login?logout";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
spring.application.name=oidc
|
||||
server.port=9000
|
||||
spring.security.oauth2.authorization-server.issuer-url: https://oidc.com
|
||||
server.servlet.session.timeout=1m
|
||||
spring.security.oauth2.authorization-server.issuer-url: https://oidc.local.com
|
||||
server.servlet.session.timeout=1m
|
||||
server.servlet.session.cookie.domain=local.com
|
||||
server.forward-headers-strategy=framework
|
||||
|
|
@ -1 +0,0 @@
|
|||
server.port=8081
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -63,9 +63,9 @@
|
|||
const oidcConfig = {
|
||||
clientId: 'a-client',
|
||||
clientSecret: 'a-secret',
|
||||
redirectUri: 'https://a.com/callback',
|
||||
tokenEndpoint: 'https://oidc.com/oauth2/token',
|
||||
userInfoEndpoint: 'https://oidc.com/userinfo'
|
||||
redirectUri: 'https://a.local.com/callback',
|
||||
tokenEndpoint: 'https://oidc.local.com/oauth2/token',
|
||||
userInfoEndpoint: 'https://oidc.local.com/userinfo'
|
||||
};
|
||||
|
||||
// 页面加载时执行
|
||||
|
|
|
|||
|
|
@ -74,10 +74,10 @@
|
|||
const oidcConfig = {
|
||||
clientId: 'a-client',
|
||||
clientSecret: 'a-secret',
|
||||
redirectUri: 'https://a.com/callback',
|
||||
authorizationEndpoint: 'https://oidc.com/oauth2/authorize',
|
||||
tokenEndpoint: 'https://oidc.com/oauth2/token',
|
||||
userInfoEndpoint: 'https://oidc.com/userinfo',
|
||||
redirectUri: 'https://a.local.com/callback',
|
||||
authorizationEndpoint: 'https://oidc.local.com/oauth2/authorize',
|
||||
tokenEndpoint: 'https://oidc.local.com/oauth2/token',
|
||||
userInfoEndpoint: 'https://oidc.local.com/userinfo',
|
||||
scope: 'openid read'
|
||||
};
|
||||
|
||||
|
|
@ -110,7 +110,7 @@
|
|||
|
||||
// 验证token
|
||||
function validateToken(token) {
|
||||
fetch('https://oidc.com/userinfo', {
|
||||
fetch('https://oidc.local.com/userinfo', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
|
|
@ -266,13 +266,21 @@
|
|||
|
||||
// 退出登录
|
||||
function logout() {
|
||||
// 通过nginx代理调用logout
|
||||
// 清理本地token
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('refresh_token');
|
||||
localStorage.removeItem('oauth_state');
|
||||
// 跳转到Spring Security默认的退出页面(GET)
|
||||
window.location.href = 'https://oidc.com/logout';
|
||||
// 获取id_token(如果有)
|
||||
const idToken = localStorage.getItem('id_token'); // 登录时保存id_token
|
||||
// 退出后跳转到首页
|
||||
const redirectUri = encodeURIComponent('https://a.local.com');
|
||||
// 拼接logout url
|
||||
let logoutUrl = `https://a.local.com/oidc-logout?post_logout_redirect_uri=${redirectUri}`;
|
||||
if (idToken) {
|
||||
logoutUrl += `&id_token_hint=${idToken}`;
|
||||
}
|
||||
// 跳转到OIDC logout端点
|
||||
window.location.href = logoutUrl;
|
||||
}
|
||||
|
||||
// 生成随机字符串
|
||||
|
|
|
|||
|
|
@ -76,11 +76,11 @@
|
|||
const oidcConfig = {
|
||||
clientId: 'a-client',
|
||||
clientSecret: 'a-secret',
|
||||
redirectUri: 'https://a.com/callback',
|
||||
authorizationEndpoint: 'https://oidc.com/oauth2/authorize',
|
||||
tokenEndpoint: 'https://oidc.com/oauth2/token',
|
||||
userInfoEndpoint: 'https://oidc.com/userinfo',
|
||||
jwksEndpoint: 'https://oidc.com/oauth2/jwks',
|
||||
redirectUri: 'https://a.local.com/callback',
|
||||
authorizationEndpoint: 'https://oidc.local.com/oauth2/authorize',
|
||||
tokenEndpoint: 'https://oidc.local.com/oauth2/token',
|
||||
userInfoEndpoint: 'https://oidc.local.com/userinfo',
|
||||
jwksEndpoint: 'https://oidc.local.com/oauth2/jwks',
|
||||
scope: 'openid read'
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,26 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIID5jCCAs6gAwIBAgIUKxk4umxo+aYVDQ483sMNIjyV3iwwDQYJKoZIhvcNAQEL
|
||||
BQAwZDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl
|
||||
aWppbmcxFDASBgNVBAoMC0RldmVsb3BtZW50MQswCQYDVQQLDAJJVDEOMAwGA1UE
|
||||
AwwFKi5jb20wHhcNMjUwNzE4MDEyNTQzWhcNMjYwNzE4MDEyNTQzWjBkMQswCQYD
|
||||
VQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEUMBIG
|
||||
A1UECgwLRGV2ZWxvcG1lbnQxCzAJBgNVBAsMAklUMQ4wDAYDVQQDDAUqLmNvbTCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANmJXA4sFsmdGU36bN5ThBAJ
|
||||
JZTY1vUaewmM41+IbSClPQEiO2/gK/tmDWBE82+IG7TEqbbLqUkORWFmp3bn0wgE
|
||||
nW3JamZMfPo43rr+3i9OmxPzQUMzIn5HTh9+QPxAPGUafbHJTcIc3ixCXHu150+6
|
||||
Mh4v/fkRk6Yog93VIwpADYEyocVn8SkTzGn2FsF5R+hu/t+bGAO+e5iYiwlaheCj
|
||||
yLfgzOuNo0jkxr1wmV2juN1CJ0nntqHaajMDqszQZXeBQ1K/UMgt2Ln2CRiQoatl
|
||||
UfeFBF/urK1VI8TrvayeMyMkXiAOxxvKLqbvLmvwKlr7iRtrE0SUsOdfjA4h788C
|
||||
AwEAAaOBjzCBjDAdBgNVHQ4EFgQUZqqbZSCLhcMWDsRQYZ5/vYjPVFMwHwYDVR0j
|
||||
BBgwFoAUZqqbZSCLhcMWDsRQYZ5/vYjPVFMwDwYDVR0TAQH/BAUwAwEB/zA5BgNV
|
||||
HREEMjAwggUqLmNvbYIFYS5jb22CBWIuY29tgghvaWRjLmNvbYIJbG9jYWxob3N0
|
||||
hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQC78BfqEOofJRjECXRLspkTRfz8LsN2
|
||||
EYu3bMvSmRjgbApY6Sh4Yg6LQUBRNkw17GMQ1WQTKVdygqZeQWYZvD1FgBHjoWwk
|
||||
3yfi9tfuWiD5XMWri7gpuF3xlizvSIgc0dR14J3w++1xB/jq030/wMPaYbgBqgIR
|
||||
8z2KtnvpwJvLnifBJLJ3a3g24hyKkZ+Ye0/0f8aLMZtUBB99QY6PMe9E3GDnQbQi
|
||||
4u4hPVzhWdxUQsoZ26NqgvobITx/hl7uKbr36NkGHUGgo9Vo+JEmy56HqZZjuwUK
|
||||
w8a4/EK0wjjPR9ooj4TWfYWfTr6Bde6v0awLENqDGJpFNacxgXGesoA4
|
||||
MIIEaTCCAtGgAwIBAgIRAO0/byq8GJJoBReBXr16yJUwDQYJKoZIhvcNAQELBQAw
|
||||
gZMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE0MDIGA1UECwwrc3Vu
|
||||
cGVuZ0BzdW5wZW5nZGVNYWNCb29rLVByby5sb2NhbCAo5a2Z6bmPKTE7MDkGA1UE
|
||||
AwwybWtjZXJ0IHN1bnBlbmdAc3VucGVuZ2RlTWFjQm9vay1Qcm8ubG9jYWwgKOWt
|
||||
mem5jykwHhcNMjUwNzE4MDc1ODM2WhcNMjcxMDE4MDc1ODM2WjBfMScwJQYDVQQK
|
||||
Ex5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxNDAyBgNVBAsMK3N1bnBl
|
||||
bmdAc3VucGVuZ2RlTWFjQm9vay1Qcm8ubG9jYWwgKOWtmem5jykwggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDgP4hfcsywJ9SbioGTODJDSG8yx1djhPp4
|
||||
yQZZ2jBqDHviF8rM+JcryTfR2Giuy9bi2ygNr18R8DDotFAp+S6LJRaS82rusiY4
|
||||
+uvfptlBKF4/E967GHtPGN59LanP8d3k3aPevpdCnAKw94doPJFmEm328h28AQYE
|
||||
v+VeQEmYbYkpuOVT5eAjDKp+GFDx+zzXQN+U4domZPHZe0rCKFtPTHzk18yXuSTZ
|
||||
q2ssAcxP27QwPPusccND3oESyF46lEbVjxRvDIYXLOHxahN7RKsSXTHaGqpGsiUH
|
||||
Si2ppRRQmTzOMxkQB9ooW1Q2yatz5gzAq0dc8ft3VLowmloas6lPAgMBAAGjazBp
|
||||
MA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAW
|
||||
gBRe+VENNEgDa5/rKTEh9XpUTvjzfTAhBgNVHREEGjAYggsqLmxvY2FsLmNvbYIJ
|
||||
bG9jYWwuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAL7KrS2TMcOKIoMzTnFLv0skL9
|
||||
evEf+9rERDPVb/J/iplaV5y1RqZjQDU9vCvsjT2igf5Xcqipu0p33pqpHwnMehDx
|
||||
M37bxonJMLOYz91PLyPZPUt76YF5NoySBshXGo6o4+PmCHpMChrspXWT36hJmfX2
|
||||
8mMwkFA6iafHy0bV9Chy6h7lzymWd7XR21xMIVT25faf8hRbx/xZ/xUf0/tWlY8Q
|
||||
wp01RWFVcP2OWgeZxttnALvgXnJPFH29S2seB2TmkuZcz6ogcuCnYH2CQCC/yL0d
|
||||
Y5YFzUqsmDaF5YUdFcwb6MTfIQOXyGLbTgBdqPgRVtxp76BTVV4nDP5IxjET6++C
|
||||
QzhJ1teqZTLdPvXIa+yqXQ/EI1NKNNRAYtmduH5kHgR1vKrQDMSHEhTaIevjN037
|
||||
2dJEdRYi47OQIocqd80StGuRNL51dAYYjB3hj0WpQpKlzc8TurB8JeVwjzn5H9pv
|
||||
A0k1s/OvborcCUguZOwVglAaVlHadmANhoel+q0=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDZiVwOLBbJnRlN
|
||||
+mzeU4QQCSWU2Nb1GnsJjONfiG0gpT0BIjtv4Cv7Zg1gRPNviBu0xKm2y6lJDkVh
|
||||
Zqd259MIBJ1tyWpmTHz6ON66/t4vTpsT80FDMyJ+R04ffkD8QDxlGn2xyU3CHN4s
|
||||
Qlx7tedPujIeL/35EZOmKIPd1SMKQA2BMqHFZ/EpE8xp9hbBeUfobv7fmxgDvnuY
|
||||
mIsJWoXgo8i34MzrjaNI5Ma9cJldo7jdQidJ57ah2mozA6rM0GV3gUNSv1DILdi5
|
||||
9gkYkKGrZVH3hQRf7qytVSPE672snjMjJF4gDscbyi6m7y5r8Cpa+4kbaxNElLDn
|
||||
X4wOIe/PAgMBAAECggEABECEFR7VfzFb6kNH13yoayvSmTs30GipGQGw/BANmgLA
|
||||
04HYyZIHKg3Pmx8d5wMxD3J8or8OWwg1YPcBtPhJDrIQZbH3K3K5SqbL67nJnAEc
|
||||
VOJ/VxHrza4VH9Z27LdQtuUyqcP2iiHIUfMmHaDrmYpZKm/jtfea/Dd0hGSDH9Mh
|
||||
dXJPKRQuQ/7ugDBJnIRYoGvdTzDSWFIzWWsC+nfeokK5paNMjkcJMFJGDVsP3lq0
|
||||
V4oH3eO2po5c/Mq4wXu+fTZthNIoQx2bkoUdG2x2aaPjNmMh6Lb6NbmBy7CBQH53
|
||||
sHVX6MRWireJMVRSJvhVrOilqDXYz90k4J8F8l0MSQKBgQDz7zGsNEQagSGF9k0p
|
||||
xpHUSU2hit6rJ/QgToKixm9ED0984zcaXDHpCcYH6oLI+YTzfGx960zgPpmrJKvc
|
||||
41FqZogGeP1GF/ZRylAz4E8OSib5LsWyzPzbczGAi+WKj0cfJHXZsXMBwL+AfLHk
|
||||
q0j/71S8qW4D5vBSyk9BHgugowKBgQDkS+eoWYPlAm+tjBVH8rgPWK/oNiI9ET1V
|
||||
K6MeH5O0gIVGwxZftZwvFXXNfJLXGkNnM/6/kXL16L7wYu9gW06fvw3gEsc4mQ+e
|
||||
5FHF7JnwfI/pnbZENwoDco1AQsGqOMZawR+OM84R4oUz4zEzgwD3aWOJudTjDumQ
|
||||
uI/hJKWq5QKBgQC3yRKyvNpG4d3BEbZHcF11BRmhSYDEkaCkKqLQQxOXwrVP0d01
|
||||
VhsgigWS90Q8aYqa7LbNFFhiZ6fdww5dqUMxGDkKL2QbyHgEXZqZyzmk+YdtnKjF
|
||||
Mx6btKmqQTzbbWHXe9/y+Xg97Nwb0Vcygz7H3akJT9ocxIVyywx1ck6uYwKBgQCJ
|
||||
aocOdpNFjanbNK66mAbideesRqllSLM6SQHuZ+NoitOuPE+DXLWeQbSe85UPlOdt
|
||||
f4afmNUx397Ooz6jKVKyJTYc4jC4iKk2Ywg1sq0WbGPTovLLLLYCTTlorMYVyAbd
|
||||
KdHsrpIjgc3b5az/7KLwSad4hzr1UUyVqAIy6vQtYQKBgQCJSsEM5hRhbQjQFBY6
|
||||
MjeAE9Y2jfZu3LYh2w/3SgO5FwvkBzc0s2Rg7d301sYryJoF720O5dwhI3p0qMru
|
||||
WoGWR7Pn3MmdDKubH8fnYhk8QDC0HLR5+7yMPNeHNfRk0VCjUkWJvAjYo/p/r3TE
|
||||
2fBQ0cw3/nOQ3QRZpfP5XRxdWQ==
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDgP4hfcsywJ9Sb
|
||||
ioGTODJDSG8yx1djhPp4yQZZ2jBqDHviF8rM+JcryTfR2Giuy9bi2ygNr18R8DDo
|
||||
tFAp+S6LJRaS82rusiY4+uvfptlBKF4/E967GHtPGN59LanP8d3k3aPevpdCnAKw
|
||||
94doPJFmEm328h28AQYEv+VeQEmYbYkpuOVT5eAjDKp+GFDx+zzXQN+U4domZPHZ
|
||||
e0rCKFtPTHzk18yXuSTZq2ssAcxP27QwPPusccND3oESyF46lEbVjxRvDIYXLOHx
|
||||
ahN7RKsSXTHaGqpGsiUHSi2ppRRQmTzOMxkQB9ooW1Q2yatz5gzAq0dc8ft3VLow
|
||||
mloas6lPAgMBAAECggEBAIfhF2I2rp7C08oX6CHruFEar/6F2Yb9CcRsksOZOSLZ
|
||||
Q6uhHQqMSxWGDKPDzNK1wxSdFS0Nqb612vz2XWjBi5lWtNIAWzgdjJmUOZ7Ae/5G
|
||||
Vq1D/f9Ce11XRWF2bOIKvZizUFtlA0SiQeM3ab4YjUXbPvSWirvjpuDz4ij1LWMF
|
||||
QeT+GM763+f+otBN3fMXxj0B2OXu2s8qxNj+iY98hGRqPs9xYYSoYDflptrJBCTw
|
||||
wLw9U8v/Lv4uglEMbNc2JvwvmqUQ30eDnrrMzlPZdlSHQyBubO0AbYtIhFQKZFvD
|
||||
sK+YCjDTu7n+WiTEolSFS563eQo5fbDFXL1G9dc/YLECgYEA7Bv+2WZ2AmvPZAGQ
|
||||
REWYq0U41Ue/JqgIXaDRR+cCHPV4aOd+GHOxVmK3Py0xB/D7Yo76+RJeJB4Vo/zp
|
||||
YsT0xSoSzgXPJIAMEjNv0NKiBqj/ayvxd/tKxXGlduyVSCfgIV8mNSCv88nf9MA1
|
||||
Mh7R4X7C/zKQkZ2LwL0ZZgr5hTkCgYEA8yO8Q/eSspoXRH6uIl3KR24FMQOw+OkC
|
||||
lAeLj3ZRZ3e0RpJNw85AxpQoWh/PVh12p7YKjM7UQHvUjfM6kcoeTzXYMqeK58/g
|
||||
xqzt67qXI/AVQsJx2oCXnA4nwyhQsU7d/deYpAFN06Zkmks8EKHq/miWEf2PfNqB
|
||||
uTuTPdWv6scCgYAkCaXdYuEyP2hZOE/fy8ugoKErFJddfBpCyDAJTH4rE2B8ipDZ
|
||||
hJcVu12C3A/2yVZlVbOC3sXVt23QKOMqeyttCJ30KjjStmShRo6TjgLDB3pszjk9
|
||||
+fIQrub1fujOKZ/xGAfJ5iJVEIQJZGj6LHAWffWfZAVi5GwXUAWXaKdrKQKBgFta
|
||||
ofIno0bX/sYNkv/2nXoZLHouGOBtLDrSWu2cVxm5MFMTxYQ8iroSENdL/GsuxtZc
|
||||
37noPHe+Dy8GpIsClkDMyl699MMEqD/92acohIFMQ7DBvmWKy2wnJWl+TFNSfrZR
|
||||
u1hj5QoRCtuuSPM240STp087Jh6TOwqOB9TD5UUhAoGARrBidxer/nmMisoVMlby
|
||||
9oHNsZt9BrrzHVW/g5AeyRobaKZObYFsZlleLu6k6amfjU8c8fU4vCePSdOuhDWg
|
||||
uJgClKZawtDV2LS2oOOcTRIhLdwDzT8XZcrrhCLskCcdSJSAVIC2JmhsLtjCuPvi
|
||||
5t/fBrOuubfljuu9ULZjMc0=
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
eyJraWQiOiIyMjYyZjU4MS0wNzRiLTRmZDctOGM2OC1mNGY3ODU1OGEyZWYiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiYXVkIjoiY2xpZW50IiwibmJmIjoxNzUyNzM5MjUyLCJzY29wZSI6WyJvcGVuaWQiXSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo5MDAwIiwiZXhwIjoxNzUyNzM5ODUyLCJpYXQiOjE3NTI3MzkyNTIsImp0aSI6ImMwNTZkNjAxLTBlZWYtNDBjYy04ODdiLTdjNjg1NDZlMzY2MSJ9.YifGLvHLe2QMInBgdJmW_us3m-Q41XX8RyaUfCv7J9gKT22wFAFtXUeXhF1bHXn9XUoPd3I33uBFclsYSs5XPqWlBhpiE-drRsZT-ZzbZ-x0YEELTkBEabYj-wivzHZZ9sVOxdV3yV7dN8-GpGJGyDglbX8tcWziZ4hCmSWIVCipb9cezBZdQYJiXe5SKb4qSmsQzF8F1CbBHuckfwXkggwVitW2YVKaeFDkV99RZRmcNzcvwVc6VU59cPr7VeirRTvjUyZKzpy9D9qtexKkSp0Fp0S4-J1NRHYh5mfaOBGSIQ52LE7c5Vz0lg23E0qCG3QvFkGF2i01fAIbEIpGLA
|
||||
Loading…
Reference in New Issue