fix: 对齐 authz 认证链路
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
- Envoy 不直接调用业务 proto 方法。
|
||||
- 新增一个内部服务 `authz-adapter`,实现 Envoy 标准 gRPC 鉴权接口。
|
||||
- `authz-adapter` 再调用现有 `user-rpc.ValidateToken` 完成鉴权。
|
||||
- `authz-adapter` 再调用现有 `user-rpc.ValidateToken` 完成会话态二次校验。
|
||||
|
||||
---
|
||||
|
||||
@@ -54,13 +54,15 @@ func (s *server) Check(ctx context.Context, req *authv3.CheckRequest) (*authv3.C
|
||||
}
|
||||
|
||||
path := httpReq.GetPath()
|
||||
method := strings.ToUpper(httpReq.GetMethod())
|
||||
|
||||
// 放行公共接口(探针、登录/注册、发送验证码)
|
||||
if path == "/healthz" ||
|
||||
path == "/api/users/login" ||
|
||||
path == "/api/users/register" ||
|
||||
path == "/api/email/verification-code/send" {
|
||||
path == "/api/v1/auth/login" ||
|
||||
path == "/api/v1/auth/register" ||
|
||||
path == "/api/v1/auth/forgot-password" ||
|
||||
path == "/api/v1/auth/reset-password" ||
|
||||
path == "/api/v1/auth/forgot-password/send" ||
|
||||
path == "/api/v1/email/verification-code/send" {
|
||||
return allow(nil), nil
|
||||
}
|
||||
|
||||
@@ -69,12 +71,17 @@ func (s *server) Check(ctx context.Context, req *authv3.CheckRequest) (*authv3.C
|
||||
return deny(401, "missing token cookie"), nil
|
||||
}
|
||||
|
||||
userID, err := parseUserIDFromPath(path)
|
||||
if err != nil {
|
||||
return deny(401, "invalid user id in path"), nil
|
||||
userIDHeader := getHeader(httpReq.GetHeaders(), "x-auth-user-id")
|
||||
if userIDHeader == "" {
|
||||
return deny(401, "missing x-auth-user-id header"), nil
|
||||
}
|
||||
|
||||
// 调用你现有业务 RPC
|
||||
userID, err := strconv.ParseInt(userIDHeader, 10, 64)
|
||||
if err != nil {
|
||||
return deny(401, "invalid x-auth-user-id"), nil
|
||||
}
|
||||
|
||||
// 调用现有业务 RPC 做会话态二次校验
|
||||
vt, err := s.userRpc.ValidateToken(ctx, &userpb.ValidateTokenReq{
|
||||
Token: token,
|
||||
UserId: userID,
|
||||
@@ -89,10 +96,7 @@ func (s *server) Check(ctx context.Context, req *authv3.CheckRequest) (*authv3.C
|
||||
Header: &core.HeaderValue{Key: "x-auth-user-id", Value: strconv.FormatInt(vt.UserId, 10)},
|
||||
},
|
||||
{
|
||||
Header: &core.HeaderValue{Key: "x-auth-role-type", Value: strconv.FormatInt(vt.RoleType, 10)},
|
||||
},
|
||||
{
|
||||
Header: &core.HeaderValue{Key: "x-auth-method", Value: method},
|
||||
Header: &core.HeaderValue{Key: "x-auth-role-type", Value: vt.RoleType},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -135,15 +139,13 @@ func extractCookie(headers map[string]string, name string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseUserIDFromPath(path string) (int64, error) {
|
||||
// 仅示例:请按你的真实路由解析,或改为从 token claim 取 userId
|
||||
seg := strings.Split(strings.Trim(path, "/"), "/")
|
||||
for i := 0; i < len(seg); i++ {
|
||||
if seg[i] == "users" && i+1 < len(seg) {
|
||||
return strconv.ParseInt(seg[i+1], 10, 64)
|
||||
func getHeader(headers map[string]string, name string) string {
|
||||
for k, v := range headers {
|
||||
if strings.EqualFold(k, name) {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return 0, strconv.ErrSyntax
|
||||
return ""
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -211,8 +213,8 @@ func main() {
|
||||
- 无 token -> 401
|
||||
- token 无效/过期 -> 401
|
||||
- 权限不足 -> 403
|
||||
5. 透传统一鉴权头:`x-auth-user-id`、`x-auth-role-type`。
|
||||
6. 灰度建议:先仅对 `/api/users` 开启,再扩展到 `/api/email`。
|
||||
5. `jwt_authn` 先注入 `x-auth-user-id`、`x-auth-is-admin`,再由 adapter 透传 `x-auth-user-id`、`x-auth-role-type`。
|
||||
6. 灰度建议:先仅对 `/api/v1/auth` 和 `/api/v1/email` 验证链路,再逐步扩展到其它业务路径。
|
||||
|
||||
> 实践建议:若保留 K8s `readiness/liveness` 探针使用 `/healthz`,请确保该路径在 `ext_authz` 上也放行,否则会出现探针 403 导致 Pod 重启。
|
||||
|
||||
@@ -221,7 +223,7 @@ func main() {
|
||||
## 5) 与当前 `jwt_authn` 的关系
|
||||
|
||||
- 可以并存:
|
||||
- 先 `jwt_authn` 快速验签
|
||||
- 先 `jwt_authn` 快速验签并注入 claim header
|
||||
- 再 `ext_authz` 做 Redis 会话态、黑名单、细粒度权限
|
||||
- 也可以只保留 `ext_authz`(由 adapter 内完成全部逻辑)。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user