Kimliği doğrulanmış istekleri tüm kullanıcılar için önbelleğe alma


9

Aynı içeriği istemek için yetkilendirilmesi gereken eşzamanlı kullanıcıların çok büyük dürtülerini ele alması gereken bir web uygulaması üzerinde çalışıyorum. Şu anki durumunda, 32 çekirdekli bir AWS örneğine bile tamamen sakat.

(Nginx'i ters proxy olarak kullandığımızı unutmayın)

En kötü durumda, kullanıcının JWT kodunu çözerek kimliğinin doğrulanıp doğrulanmadığını kontrol etmeliyiz. Bu, PHP-FPM ve OpCache etkin olsa bile , en çok anlaşılan Laravel 4'ü yavaşlatmamızı gerektirir. Bu çoğunlukla ağır önyükleme aşamasından kaynaklanmaktadır.

"Bunun bir sorun olacağını bilseydiniz neden ilk etapta PHP ve Laravel kullandınız?" - ama şimdi bu karara geri dönmek için çok geç!

Olası çözüm

Öne sürülen bir çözüm, Auth modülünü Laravel'den, sorumluluğu JWT kodunu çözmek ve kullanıcının kimliğinin doğrulanıp doğrulanmayacağına karar vermek olan hafif bir harici modüle (C gibi hızlı bir şekilde yazılmış) çıkarmaktır.

Bir isteğin akışı şu şekildedir:

  1. Önbellek isabet edip etmediğini kontrol edin (PHP'ye normal şekilde geçmezse)
  2. Simgenin kodunu çöz
  3. Geçerli olup olmadığını kontrol edin
  4. Eğer geçerli , önbellekten hizmet
  5. Eğer geçersiz , Nginx söyle ve sonra Nginx sonra normal ile anlaşmaya PHP isteği geçecek.

Bu, bu isteği tek bir kullanıcıya sunduktan sonra PHP'yi vurmamamızı ve bunun yerine JWT'leri ve bu tür bir yetkilendirme ile gelen diğer uyarıları çözmek için hafif bir modüle ulaşmamızı sağlayacaktır.

Hatta bu kodu doğrudan bir Nginx HTTP genişletme modülü olarak yazmayı düşünüyordum.

Endişeler

Benim endişem, bunu daha önce hiç görmemiştim ve daha iyi bir yol olup olmadığını merak ettim.

Ayrıca, kullanıcıya kullanıcıya özel içerik eklediğinizde, bu yöntemi tamamen öldürür.

Doğrudan Nginx'te daha basit bir çözüm var mı? Yoksa Vernik gibi daha özel bir şey mi kullanmalıyız?

Sorularım:

Yukarıdaki çözüm mantıklı mı?

Buna normal olarak nasıl yaklaşılıyor?

Benzer veya daha iyi bir performans kazancı elde etmenin daha iyi bir yolu var mı?


Benzer bir sorunla boğuşuyorum. Birkaç fikir a) Nginx auth_request, kimlik doğrulama mikro hizmetinize bir Nginx modülü geliştirme ihtiyacını hafifletebilir. b) Alternatif olarak, mikro hizmetiniz kimliği doğrulanmış kullanıcıları herkese açık, önbelleğe alınabilir ve tahmin edilemeyen, ancak PHP arka ucu tarafından sınırlı bir süre (önbellek süresi) geçerli olacak şekilde geçici bir URL'ye yönlendirebilir. Bu, bir miktar güvenliği feda eder, geçici URL güvenilmeyen bir kullanıcıya sızdırılırsa, içeriğe OAuth Taşıyıcı Jetonu gibi sınırlı bir süre için erişebilirler.
James

Buna bir çözüm buldunuz mu? Aynı şeyle karşı karşıyayım
timbroder

Geniş bir optimize edilmiş arka uç düğümleri kümesine sahip olarak, yükle başa çıkabildiğimiz ortaya çıkıyor - ancak bu yaklaşımın uzun vadeli büyük bir maliyet tasarrufu çözümü olduğuna yüksek güvenim var. Önceden sunabileceğiniz bazı yanıtları biliyorsanız, istek akışından önce önbelleği ısıtırsanız, arka uç kaynak tasarrufu ve güvenilirlik kazancı çok yüksek olacaktır.
iamyojimbo

Yanıtlar:


9

Benzer bir sorunu çözmeye çalışıyorum. Kullanıcılarımın yaptıkları her istek için kimliğinin doğrulanması gerekiyor. Kullanıcıların arka uç uygulaması (JWT jetonunun doğrulanması) tarafından en az bir kez kimlik doğrulaması yapmaya odaklandım, ancak bundan sonra artık arka uca ihtiyacım olmaması gerektiğine karar verdim.

Varsayılan olarak dahil olmayan herhangi bir Nginx eklentisi gerektirmekten kaçınmayı seçtim. Aksi takdirde nginx-jwt veya Lua komut dosyasını kontrol edebilirsiniz ve bunlar muhtemelen harika çözümler olacaktır.

Kimlik doğrulama adresleme

Şimdiye kadar aşağıdakileri yaptım:

  • Kullanarak kimlik doğrulamasını Nginx'e devretti auth_request. Bu internal, isteği arka uç belirteç doğrulama uç noktama ileten bir konumu çağırır . Bu tek başına, çok sayıda onaylamanın ele alınması sorununu ele almaz.

  • Belirteç doğrulamasının sonucu bir proxy_cache_key "$cookie_token";yönerge kullanılarak önbelleğe alınır . Başarılı jeton doğrulaması üzerine, arka uç Cache-ControlNginx'e jetonu yalnızca 5 dakikaya kadar önbelleğe almasını söyleyen bir yönerge ekler . Bu noktada, bir kez onaylanan herhangi bir doğrulama jetonu önbellektir, aynı kullanıcı / jetondan sonraki istekler artık doğrulama arkasına dokunmaz!

  • Arka uç uygulamamı geçersiz belirteçlerle potansiyel sellere karşı korumak için, arka uç uç noktam 401 değerini döndürdüğünde reddedilen doğrulamaları da önbelleğe alırım. Bunlar, Nginx önbelleğini bu tür isteklerle doldurmayı önlemek için yalnızca kısa bir süre için önbelleğe alınır.

401 döndürerek (Nginx tarafından da önbelleğe alınır) bir jetonu geçersiz kılan oturum kapatma uç noktası gibi birkaç ek iyileştirme ekledim, böylece kullanıcı oturumu kapatırsa, simge süresi dolmamış olsa bile artık kullanılamaz.

Ayrıca, benim Nginx önbellek her jeton, bir JSON nesnesi olarak ilişkili kullanıcı içerir, bu bilgi gerekiyorsa DB onu getirmekten beni kurtarır; ve ayrıca jetonun şifresini çözmekten de kurtarıyor.

Simge ömrü ve yenileme simgeleri hakkında

5 dakika sonra jetonun önbellekte süresi dolar, bu nedenle arka uç tekrar sorgulanır. Bu, bir simgeyi geçersiz kılabildiğinizden emin olmak içindir, çünkü kullanıcı oturumu kapatır, çünkü güvenliği ihlal edilmiştir vb. Arka uçta doğru uygulama ile bu tür periyodik revalidasyon, yenileme jetonlarını kullanmamı engeller.

Yeni bir erişim belirteci istemek için geleneksel olarak yenileme belirteçleri kullanılır; bunlar arka ucunuzda depolanır ve bu özel kullanıcı için veritabanında bulunana uyan bir yenileme belirteci isteğiyle yapıldığını doğrularsınız. Kullanıcı oturumu kapatırsa veya belirteçler ele geçirilirse, geçersiz kılınmış yenileme belirtecini kullanarak yeni bir belirteç isteğinin başarısız olması için DB'nizdeki yenileme belirtecini siler / geçersiz kılabilirsiniz.

Kısacası, yenileme simgeleri genellikle uzun bir geçerliliğe sahiptir ve her zaman arka uca göre kontrol edilir. Çok kısa geçerliliği olan (birkaç dakika) erişim belirteçleri oluşturmak için kullanılırlar. Bu erişim belirteçleri normalde arka ucunuza ulaşır, ancak yalnızca imza ve son kullanma tarihlerini kontrol edersiniz.

Burada kurulumumda, hem erişim belirteci hem de yenileme belirteciyle aynı role ve özelliklere sahip daha uzun geçerliliğe sahip (saat veya gün olabilir) belirteçler kullanıyoruz. Doğrulama ve geçersiz kılma Nginx tarafından önbelleğe alındığından, arka uç tarafından her 5 dakikada bir tamamen doğrulanır. Bu nedenle, ilave karmaşıklık olmadan yenileme jetonlarını (bir jetonu hızla geçersiz kılabilmek) kullanmanın avantajını koruyoruz. Ve basit doğrulama, yalnızca imza ve son kullanma tarihi kontrolü için kullanılsa bile, Nginx önbelleğinden en az 1 derece daha büyük olan arka ucunuza asla ulaşmaz.

Bu kurulumla, gelen tüm istekler auth_requestdokunmadan önce Nginx yönergesine ulaştığından arka ucumdaki kimlik doğrulamasını devre dışı bırakabilirim .

Kaynak başına yetkilendirme yapmanız gerekiyorsa bu sorunu tam olarak çözmez, ancak en azından temel yetkilendirme bölümünü kaydettiniz. Ayrıca, Nginx önbelleğe alınmış kimlik doğrulaması veri içerebileceğinden ve arka uca geri gönderebileceğinden, belirtecin şifresini çözmeyi önleyebilir veya belirteç verilerine erişmek için DB araması yapabilirsiniz.

Şimdi, en büyük endişem farkında olmadan güvenlikle ilgili bariz bir şeyi kırıyor olabilirim. Bununla birlikte, alınan herhangi bir belirteç, Nginx tarafından önbelleğe alınmadan önce en az bir kez hala doğrulanır. Herhangi bir temperlenmiş simge farklı olacaktır, bu yüzden önbellek anahtarı da farklı olacağından önbelleğe basmaz.

Ayrıca, gerçek bir dünya kimlik doğrulamasının ek bir Nonce veya başka bir şey üreterek (ve doğrulayarak) jeton çalmaya karşı savaşacağından bahsetmeye değer.

İşte benim app için benim Nginx yapılandırma basitleştirilmiş bir özü:

# Cache for internal auth checks
proxy_cache_path /usr/local/var/nginx/cache/auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=10m use_temp_path=off;
# Cache for content
proxy_cache_path /usr/local/var/nginx/cache/resx levels=1:2 keys_zone=content_cache:16m max_size=128m inactive=5m use_temp_path=off;
server {
    listen 443 ssl http2;
    server_name ........;

    include /usr/local/etc/nginx/include-auth-internal.conf;

    location /api/v1 {
        # Auth magic happens here
        auth_request         /auth;
        auth_request_set     $user $upstream_http_X_User_Id;
        auth_request_set     $customer $upstream_http_X_Customer_Id;
        auth_request_set     $permissions $upstream_http_X_Permissions;

        # The backend app, once Nginx has performed internal auth.
        proxy_pass           http://127.0.0.1:5000;
        proxy_set_header     X-User-Id $user;
        proxy_set_header     X-Customer-Id $customer;
        proxy_set_header     X-Permissions $permissions;

        # Cache content
        proxy_cache          content_cache;
        proxy_cache_key      "$request_method-$request_uri";
    }
    location /api/v1/Logout {
        auth_request         /auth/logout;
    }

}

Şimdi, iç /authuç nokta için yukarıda belirtilen yapılandırma özeti şöyledir /usr/local/etc/nginx/include-auth-internal.conf:

# Called before every request to backend
location = /auth {
    internal;
    proxy_cache             auth_cache;
    proxy_cache_methods     GET HEAD POST;
    proxy_cache_key         "$cookie_token";
    # Valid tokens cache duration is set by backend returning a properly set Cache-Control header
    # Invalid tokens are shortly cached to protect backend but not flood Nginx cache
    proxy_cache_valid       401 30s;
    # Valid tokens are cached for 5 minutes so we can get the backend to re-validate them from time to time
    proxy_cache_valid       200 5m;
    proxy_pass              http://127.0.0.1:1234/auth/_Internal;
    proxy_set_header        Host ........;
    proxy_pass_request_body off;
    proxy_set_header        Content-Length "";
    proxy_set_header        Accept application/json;
}

# To invalidate a not expired token, use a specific backend endpoint.
# Then we cache the token invalid/401 response itself.
location = /auth/logout {
    internal;
    proxy_cache             auth_cache;
    proxy_cache_key         "$cookie_token";
    # Proper caching duration (> token expire date) set by backend, which will override below default duration
    proxy_cache_valid       401 30m;
    # A Logout requests forces a cache refresh in order to store a 401 where there was previously a valid authorization
    proxy_cache_bypass      1;

    # This backend endpoint always returns 401, with a cache header set to the expire date of the token
    proxy_pass              http://127.0.0.1:1234/auth/_Internal/Logout;
    proxy_set_header        Host ........;
    proxy_pass_request_body off;
}

.

İçerik sunumunu adresleme

Şimdi kimlik doğrulama verilerden ayrılmıştır. Her kullanıcı için aynı olduğunu söylediğiniz için, içeriğin kendisi de Nginx tarafından önbelleğe alınabilir (benim örneğimde, content_cachebölgede).

Ölçeklenebilirlik

Bu senaryo, bir Nginx sunucunuz olduğunu varsayarak kutudan çıkar çıkmaz çalışır. Gerçek bir dünya senaryosunda muhtemelen yüksek kullanılabilirliğe sahip olursunuz, yani birden fazla Nginx örneği, potansiyel olarak (Laravel) arka uç uygulamanızı da barındırır. Bu durumda, kullanıcılarınızın istediği herhangi bir istek Nginx sunucularınızdan herhangi birine gönderilebilir ve hepsi belirteci yerel olarak önbelleğe alana kadar, doğrulamak için arka ucunuza ulaşmaya devam eder. Az sayıda sunucu için, bu çözümü kullanmak yine de büyük faydalar sağlayacaktır.

Bununla birlikte, birden fazla Nginx sunucusunda (ve böylece önbelleklerde), sunucu tarafında oturum kapatma yeteneğini kaybettiğinizi, çünkü hepsinde token önbelleğini temizleyemediğinizi (bir yenileme zorlayarak) unutmayın. /auth/logoutbenim örneğimde. Yalnızca arka ucunuzu yakında sorgulanmaya zorlayacak ve Nginx'e isteğin reddedildiğini söyleyecek olan 5mn jeton önbellek süresi kaldı. Kısmi bir geçici çözüm, oturumu kapatırken istemcideki belirteç başlığını veya çerezini silmektir.

Herhangi bir yorum çok memnuniyetle karşılanacaktır ve takdir edilecektir!


Çok daha fazla oy almalısın! Çok faydalı, teşekkürler!
Gershon Papi

"401 döndürerek (Nginx tarafından da önbelleğe alınır) bir jetonu geçersiz kılan oturum kapatma uç noktası gibi birkaç ek iyileştirme ekledim, böylece kullanıcı oturumu kapatırsa simge artık süresi dolmasa bile kullanılamaz. " - Bu akıllıca! , ancak aslında arka ucunuzdaki jetonu kara listeye alıyor musunuz, böylece önbellek bozulursa veya kullanıcı yine de bu jetonla giriş yapamaz mı?
gaurav5430

"Ancak, birden çok Nginx sunucusunda (ve böylece önbelleklerde), sunucu tarafında oturum kapatma yeteneğini kaybettiğinizi, çünkü hepsinde token önbelleğini temizleyemediğinizi (yenileyerek), like / auth / logout benim örneğimde var. " detaylandırabilir misin
gaurav5430
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.