Ory Hydra là một máy chủ OAuth2 và OpenID Connect mã nguồn mở phổ biến cung cấp xác thực và ủy quyền an toàn cho các ứng dụng. Một trong những thách thức chính trong việc xây dựng máy chủ OAuth2 có thể mở rộng và hoạt động hiệu quả là quản lý lớp lưu trữ liên tục, bao gồm lưu trữ và truy xuất dữ liệu từ cơ sở dữ liệu.
Ory đã được một nhà cung cấp dịch vụ phổ biến tiếp cận để tối ưu hóa hiệu suất trong hệ thống xác thực của họ ở mức tải cao. Họ thường phải vật lộn để đối phó với lượng cấp phép khổng lồ trong thời gian cao điểm (hơn 600 lần đăng nhập/giây). Tìm kiếm một giải pháp khác, họ bắt đầu đánh giá Ory Hydra, điều tra xem nó có thể xử lý số lượng tài trợ này hay không. Sau khi liên hệ với nhóm, chúng tôi bắt đầu nghiên cứu các cách cải thiện hiệu suất tổng thể để làm cho Ory Hydra nhanh hơn và có thể mở rộng hơn bao giờ hết. Chìa khóa thành công là tái thiết kế các phần của lớp kiên trì của Hydra để giảm lưu lượng ghi vào cơ sở dữ liệu, chuyển sang luồng OAuth2 tạm thời.
Một trong những phần cốt lõi của công việc này là di chuyển một lượng lớn trạng thái luồng OAuth2 tạm thời, được trao đổi giữa ba bên tham gia vào luồng OAuth2, từ máy chủ sang máy khách. Thay vì duy trì trạng thái tạm thời cho cơ sở dữ liệu, trạng thái hiện được chuyển giữa các bên dưới dạng cookie được mã hóa AEAD hoặc tham số truy vấn được mã hóa AEAD trong URL chuyển hướng. AEAD là viết tắt của mã hóa được xác thực với dữ liệu được liên kết, có nghĩa là dữ liệu được bảo mật và cũng không thể bị giả mạo nếu không biết khóa bí mật (đối xứng).
Luồng sau đó chỉ được duy trì trong cơ sở dữ liệu một lần khi có sự đồng ý cuối cùng.
Sự thay đổi này có một số lợi ích. Đầu tiên, nó làm giảm lượng dữ liệu cần lưu trữ trong cơ sở dữ liệu, do đó làm giảm lưu lượng ghi. Thứ hai, nó loại bỏ sự cần thiết của nhiều chỉ số trên bảng lưu lượng đã được sử dụng trước đây trong quá trình trao đổi.
Phần có liên quan của quy trình OAuth2 mà chúng tôi muốn tối ưu hóa là sự trao đổi giữa ứng dụng khách (hành động thay mặt cho người dùng), Hydra (Máy chủ ủy quyền OAuth2 của Ory) và màn hình đăng nhập và đồng ý. Khi khách hàng yêu cầu mã ủy quyền thông qua Cấp mã ủy quyền , trước tiên, người dùng sẽ được chuyển hướng đến giao diện người dùng đăng nhập để xác thực và sau đó đến giao diện người dùng đồng ý để cấp quyền truy cập vào dữ liệu của người dùng (chẳng hạn như địa chỉ email hoặc thông tin hồ sơ).
Dưới đây là một sơ đồ trình tự của trao đổi. Quan sát rằng mỗi giao diện người dùng nhận một CHALLENGE
như một phần của tham số URL (bước 3 và 12), sau đó sử dụng CHALLENGE
này làm tham số để truy xuất thêm thông tin (bước 4 và 13). Cuối cùng, cả hai giao diện người dùng đều chấp nhận hoặc từ chối yêu cầu của người dùng, thường dựa trên tương tác của người dùng với giao diện người dùng (từ bước 6 đến 8 và 15 đến 17). Hợp đồng API này giữ cho Ory Hydra không đầu và tách rời khỏi giao diện người dùng tùy chỉnh.
Để giảm quyền truy cập cơ sở dữ liệu, giờ đây chúng tôi chuyển dưới dạng LOGIN_CHALLENGE
, LOGIN_VERIFIER
, CONSENT_CHALLENGE
và CONSENT_VERIFIER
một luồng được mã hóa AEAD. Bằng cách này, chúng tôi dựa vào các bên tham gia vào luồng OAuth2 để chuyển trạng thái có liên quan.
Trước | Sau đó |
---|---|
Các thử thách và trình xác minh đăng nhập và chấp thuận là các UUID ngẫu nhiên được lưu trữ trong cơ sở dữ liệu. | Các thử thách và trình xác minh đăng nhập và chấp thuận là quy trình được mã hóa AEAD. |
Việc chấp nhận hoặc từ chối yêu cầu từ giao diện người dùng liên quan đến việc tra cứu cơ sở dữ liệu cho thử thách cụ thể. | Việc chấp nhận hoặc từ chối yêu cầu từ giao diện người dùng liên quan đến việc giải mã luồng trong thử thách và tạo luồng cập nhật như một phần của trình xác minh. |
Vì Ory Hydra là mã nguồn mở nên bạn có thể xem lại các thay đổi mã trong kho lưu trữ Ory GitHub. Đây là cam kết có liên quan.
Đây là nơi chúng tôi mã hóa luồng trong các thách thức và xác minh cụ thể:
// ToLoginChallenge converts the flow into a login challenge. func (f *Flow) ToLoginChallenge(ctx context.Context, cipherProvider CipherProvider) (string, error) { return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsLoginChallenge) } // ToLoginVerifier converts the flow into a login verifier. func (f *Flow) ToLoginVerifier(ctx context.Context, cipherProvider CipherProvider) (string, error) { return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsLoginVerifier) } // ToConsentChallenge converts the flow into a consent challenge. func (f *Flow) ToConsentChallenge(ctx context.Context, cipherProvider CipherProvider) (string, error) { return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsConsentChallenge) } // ToConsentVerifier converts the flow into a consent verifier. func (f *Flow) ToConsentVerifier(ctx context.Context, cipherProvider CipherProvider) (string, error) { return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsConsentVerifier) }
Sau đó, trong kho lưu trữ cơ sở dữ liệu (kho lưu trữ cơ sở dữ liệu của chúng tôi), chúng tôi giải mã luồng có trong thử thách. Ví dụ: đây là mã để xử lý thách thức về sự đồng ý:
func (p *Persister) GetFlowByConsentChallenge(ctx context.Context, challenge string) (*flow.Flow, error) { ctx, span := prTracer(ctx).Tracer().Start(ctx, "persistence.sql.GetFlowByConsentChallenge") defer span.End() // challenge contains the flow. f, err := flowctx.Decode[flow.Flow](ctx, prFlowCipher(), challenge, flowctx.AsConsentChallenge) if err != nil { return nil, errorsx.WithStack(x.ErrNotFound) } if f.NID != p.NetworkID(ctx) { return nil, errorsx.WithStack(x.ErrNotFound) } if f.RequestedAt.Add(p.config.ConsentRequestMaxAge(ctx)).Before(time.Now()) { return nil, errorsx.WithStack(fosite.ErrRequestUnauthorized.WithHint("The consent request has expired, please try again.")) } return f, nil }
Hãy xem xét tác động của các thay đổi khi so sánh với mã không có tối ưu hóa:
Các luồng hiện nhanh hơn nhiều và ít nói chuyện hơn với cơ sở dữ liệu.
Bằng cách giới thiệu một chỉ mục mới trên bảng hydra_oauth2_flow
, chúng tôi có thể tăng thông lượng và giảm mức sử dụng CPU trên PostgreSQL. Ảnh chụp màn hình bên dưới cho thấy việc thực hiện các điểm chuẩn mà không có chỉ số được cải thiện trong đó mức sử dụng CPU tăng vọt lên 100% và với các chỉ số được cải thiện, mức sử dụng CPU vẫn ở mức dưới 10%.
Với các chỉ số mới được thêm vào, việc sử dụng CPU (các thanh màu xanh lá cây) sẽ bị loại bỏ, giúp giảm khả năng BufferLocks và các sự cố liên quan:
Các thay đổi về mã và cơ sở dữ liệu đã giảm tổng số vòng lặp tới cơ sở dữ liệu xuống 4-5 lần (tùy thuộc vào số lượng bộ nhớ đệm được thực hiện) và giảm khả năng ghi cơ sở dữ liệu khoảng 50%.
Điểm chuẩn triển khai mới trên Microsoft Azure với các thông số kỹ thuật sau:
Dịch vụ | Cấu hình | Tổng số kết nối SQL tối đa | ghi chú |
---|---|---|---|
Ory Hydra Consent App Ứng dụng khách OAuth2 rakyll/hey (công cụ điểm chuẩn http) | 3x Chuẩn_D32as_v4; Chuẩn Nam Trung Mỹ 5x_D8s_v3; Nam Trung Mỹ | 512 | Mọi máy ảo đều chạy tất cả các quy trình được đề cập. |
PostgreSQL 14 trong cấu hình HA | Tối ưu hóa bộ nhớ, E64ds_v4, 64 vCore, RAM 432 GiB, bộ nhớ 32767 GiB; Nam Trung Mỹ | | RAM đánh bại CPU. |
Ory có thể thực hiện tới 1090 lần đăng nhập/giây ở mức cao nhất và 800 lần đăng nhập/giây liên tục ở cấu hình trên. Điều này có thể thực hiện được bằng cách làm cho luồng không trạng thái và tối ưu hóa các chỉ số trong các truy vấn được sử dụng thường xuyên.
Công việc tối ưu hóa hiệu suất do nhóm Ory thực hiện đã dẫn đến sự cải thiện đáng kể về hiệu suất và khả năng mở rộng của Hydra. Bằng cách giảm lưu lượng ghi vào cơ sở dữ liệu và cải thiện cơ sở mã cũng như các phần phụ thuộc, Hydra giờ đây nhanh hơn và phản hồi nhanh hơn bao giờ hết. Bằng cách cải thiện các chỉ số, Hydra giờ đây mở rộng quy mô hiệu quả hơn nhiều với số lượng phiên bản.
Trong tương lai, chúng tôi sẽ tiếp tục tối ưu hóa phần mềm của Ory để xử lý nhiều lưu lượng hơn nữa. Chúng tôi tin rằng có thể tăng thông lượng gấp 5 lần trên một nút PostgreSQL duy nhất bằng cách tối ưu hóa mô hình dữ liệu.
Nếu bạn đang xây dựng máy chủ OAuth2, chúng tôi thực sự khuyên bạn nên thử triển khai OpenID Connect và OAuth2 được chứng nhận đầy đủ của Ory: Ory OAuth2 – dịch vụ được quản lý hoàn toàn của chúng tôi chạy trên Mạng Ory toàn cầu, dựa trên nguồn mở Ory Hydra – đã sử dụng các tối ưu hóa được mô tả trong bài viết này và thiết lập nó chỉ mất vài phút!
Cũng được xuất bản ở đây .