Σε ένα σύγχρονο ASP.NET Core backend, το authentication και το authorization δεν είναι μία ενιαία διαδικασία, ούτε ένα σημείο κώδικα που “ανεβάζεις και τελείωσες”. Είναι μια ροή από στάδια, όπου κάθε στάδιο έχει διαφορετική ευθύνη και κανένα δεν πρέπει να μπερδεύεται με το άλλο.
Όταν αυτά τα στάδια ανακατεύονται, το αποτέλεσμα είναι σύστημα δύσκολο να συντηρηθεί, δύσκολο να επεκταθεί και συνήθως αργό. Όταν όμως είναι σωστά διαχωρισμένα, το σύστημα γίνεται προβλέψιμο, γρήγορο και scalable.
Το πρώτο σημείο: Middleware και JWT validation
Κάθε request που έρχεται στο API δεν φτάνει ποτέ απευθείας στον controller. Περνάει πρώτα από το middleware pipeline της ASP.NET Core.
Εκεί βρίσκεται το authentication middleware, το οποίο είναι υπεύθυνο να ελέγξει το JWT token πριν συμβεί οτιδήποτε άλλο.
Σε αυτό το σημείο γίνεται ο έλεγχος της υπογραφής του token, του issuer, του audience και κυρίως του χρόνου ζωής του. Αν το token έχει λήξει, το request απορρίπτεται άμεσα και επιστρέφεται 401 Unauthorized χωρίς να εκτελεστεί τίποτα άλλο.
Αυτό σημαίνει πρακτικά ότι ο χρήστης “κόβεται” πριν φτάσει σε business logic, πριν φτάσει σε authorization, πριν γίνει οποιοδήποτε database call. Το middleware λειτουργεί σαν απόλυτο security gate.
Αν το token είναι έγκυρο, τότε δημιουργείται το HttpContext.User και το request συνεχίζει τη διαδρομή του μέσα στο pipeline.
Τι σημαίνει στην πράξη η λήξη του JWT
Η λήξη του JWT δεν είναι κάτι που το χειρίζεται η εφαρμογή ως business logic. Είναι καθαρά security rule.
Όταν ο χρόνος exp περάσει, το middleware απλά απορρίπτει το request. Δεν υπάρχει session, δεν υπάρχει “logout server side”, δεν υπάρχει κάποια επιπλέον διαδικασία. Το token θεωρείται άκυρο και τελείωσε εκεί η διαδρομή.
Ο client είναι αυτός που πρέπει να αντιδράσει σε αυτή την κατάσταση, είτε κάνοντας refresh token call είτε ζητώντας login ξανά.
Μετά το middleware: αρχίζει το authorization
Μόλις το JWT περάσει τον έλεγχο, το σύστημα γνωρίζει πλέον ποιος είναι ο χρήστης. Αυτό όμως δεν σημαίνει ότι ξέρει τι επιτρέπεται να κάνει.
Εδώ ξεκινά το authorization κομμάτι, το οποίο δεν είναι ένα απλό if statement, ούτε ένα δεύτερο middleware που “τρέχει μόνο του”. Είναι ένα policy-based σύστημα που ενεργοποιείται όταν ένα endpoint απαιτεί συγκεκριμένο δικαίωμα.
Για παράδειγμα, όταν ένα endpoint έχει attribute όπως “Orders.Read”, δεν σημαίνει ότι το framework απλά κοιτάει roles. Σημαίνει ότι ενεργοποιείται μια διαδικασία ελέγχου που θα αποφασίσει δυναμικά αν ο συγκεκριμένος χρήστης μπορεί να εκτελέσει αυτή την ενέργεια.
Πώς υλοποιείται στην πράξη το authorization
Στην πράξη, το authorization εκφράζεται μέσω policies και handlers. Το endpoint δηλώνει τι απαιτείται, και ο handler αποφασίζει αν ο χρήστης το πληροί.
Όταν ένα request φτάνει σε ένα endpoint με policy, η ASP.NET ενεργοποιεί τον αντίστοιχο authorization handler. Εκεί γίνεται η πραγματική αξιολόγηση.
Ο handler δεν παίρνει την απόφαση από μόνος του. Ζητάει δεδομένα από ένα service, το PermissionService, το οποίο είναι υπεύθυνο να βρει τα δικαιώματα του χρήστη.
Πού αποθηκεύονται τα permissions
Τα permissions δεν πρέπει να βρίσκονται μέσα στο JWT. Ο λόγος είναι ότι αλλάζουν συχνά, πρέπει να μπορούν να ανακληθούν και δεν πρέπει να “παγώνουν” μέσα σε ένα token.
Αντίθετα, αποθηκεύονται σε βάση δεδομένων σε κανονικό relational μοντέλο.
Υπάρχουν users, roles και permissions. Οι χρήστες συνδέονται με ρόλους και οι ρόλοι συνδέονται με permissions. Έτσι το σύστημα γίνεται δυναμικό και μπορεί να αλλάξει ανά πάσα στιγμή χωρίς να χρειάζεται reissue όλων των tokens.
PermissionService: το σημείο που διαβάζονται τα δικαιώματα
Το PermissionService είναι το μοναδικό κομμάτι της εφαρμογής που γνωρίζει πώς να πάρει τα permissions ενός χρήστη από τη βάση δεδομένων.
Όταν καλείται, κάνει join μεταξύ users, roles και permissions και επιστρέφει τη λίστα των δικαιωμάτων.
Αυτό το κομμάτι είναι business data access layer και όχι security logic. Η απόφαση για το αν επιτρέπεται μια ενέργεια γίνεται αλλού, όχι εδώ.
Cache γιατί υπάρχει και πού μπαίνει
Αν κάθε request χτυπάει τη βάση για permissions, το σύστημα θα αρχίσει πολύ γρήγορα να υποφέρει σε load.
Γι’ αυτό μπαίνει cache.
Η cache δεν αλλάζει τη λογική του συστήματος. Δεν αποφασίζει τίποτα. Απλώς αποθηκεύει προσωρινά το αποτέλεσμα ενός προηγούμενου query ώστε να μην χρειάζεται να επαναληφθεί.
Το PermissionService πρώτα κοιτάει αν υπάρχουν ήδη τα permissions του χρήστη στην cache. Αν υπάρχουν, τα επιστρέφει αμέσως. Αν δεν υπάρχουν, τότε πηγαίνει στη βάση, τα φέρνει και τα αποθηκεύει για επόμενες κλήσεις.
Έτσι το πρώτο request είναι πιο “βαρύ”, αλλά όλα τα επόμενα είναι σχεδόν άμεσα.
Όλη η ροή μαζί
Όταν όλα τα κομμάτια συνδεθούν μεταξύ τους, η ροή του request είναι καθαρή.
Το request έρχεται στο σύστημα και περνάει πρώτα από το middleware. Εκεί γίνεται ο έλεγχος του JWT. Αν το token έχει λήξει, το request σταματάει αμέσως. Αν είναι valid, δημιουργείται το user context και το request συνεχίζει.
Στη συνέχεια ενεργοποιείται το authorization layer, το οποίο βλέπει ότι το συγκεκριμένο endpoint απαιτεί συγκεκριμένα permissions. Ο authorization handler καλείται και ζητάει τα permissions του χρήστη από το PermissionService.
Το PermissionService πρώτα κοιτάει στην cache. Αν τα δεδομένα υπάρχουν εκεί, τα επιστρέφει άμεσα. Αν όχι, κάνει query στη βάση δεδομένων, τα αποθηκεύει στην cache και τα επιστρέφει.
Αν ο χρήστης έχει το απαιτούμενο permission, το request συνεχίζει στον controller και από εκεί μπορεί να καλέσει άλλα microservices ή να εκτελέσει business logic.
Γιατί αυτό δεν γίνεται bottleneck
Το κρίσιμο σημείο είναι το PermissionService και η βάση δεδομένων. Θεωρητικά αυτό θα μπορούσε να γίνει bottleneck, αλλά στην πράξη δεν γίνεται, γιατί σχεδόν ποτέ δεν χτυπάει τη βάση.
Η cache απορροφά τη συντριπτική πλειοψηφία των requests, οπότε η βάση χρησιμοποιείται μόνο σε cache miss ή όταν αλλάξουν τα permissions.
Αυτό σημαίνει ότι το authorization δεν είναι hot path προς τη βάση. Είναι hot path προς τη cache, η οποία είναι εξαιρετικά γρήγορη.
Έτσι το σύστημα παραμένει σταθερό ακόμα και υπό μεγάλο φορτίο.
Διάγραμμα
┌───────────────────────────────────────────────────────────┐
│ Client / SPA │
└───────────────────────┬───────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ HTTP Request │
│ Authorization: Bearer JWT │
└───────────────────────┬───────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ Authentication Middleware │
│ │
│ Validate JWT Signature │
│ Validate Issuer / Audience │
│ Validate Expiration (exp) │
└───────────────────────┬───────────────────────────────────┘
│
┌─────────────┴─────────────┐
│ │
▼ ▼
Token Invalid Token Valid
│ │
▼ ▼
401 Unauthorized HttpContext.User Created
│ │
│ ▼
│ ┌──────────────────────────────┐
│ │ Token Revocation Check │
│ │ │
│ │ Redis / Cache / DB Lookup │
│ └──────────────┬───────────────┘
│ │
│ ┌─────────────┴────────────┐
│ │ │
│ ▼ ▼
│ Revoked Active
│ │ │
│ ▼ ▼
│ 401 Unauthorized Authorization Layer
│ │
│ ▼
│ ┌────────────────────────────┐
│ │ Policy / Handler │
│ │ Orders.Read Permission │
│ └─────────────┬──────────────┘
│ │
│ ▼
│ ┌────────────────────────────┐
│ │ PermissionService │
│ └─────────────┬──────────────┘
│ │
│ ▼
│ ┌────────────────────────────┐
│ │ Cache │
│ │ (MemoryCache / Redis) │
│ └─────────────┬──────────────┘
│ │
│ ┌─────────────────┴────────────┐
│ │ │
│ ▼ ▼
│ Cache Hit Cache Miss
│ │ │
│ │ ▼
│ │ ┌────────────────────────┐
│ │ │ Database │
│ │ │ Users / Roles / Perms │
│ │ └───────────┬────────────┘
│ │ │
│ └──────────────┬───────────┘
│ │
│ ▼
│ Permission Decision
│ │
│ ┌───────────┴───────────┐
│ │ │
│ ▼ ▼
│ Allow Deny
│ │ │
│ ▼ ▼
│ Controller 403 Forbidden
│ │
│ ▼
│ Orders API / Customers API
│
│
└─────────────────────────────────────────────────
ACCESS TOKEN EXPIRES
│
▼
Client receives 401
│
▼
POST /auth/refresh
│
▼
Refresh Token Validation
│
▼
Refresh Token Store
(DB / Redis)
│
┌─────────────┴─────────────┐
│ │
▼ ▼
Invalid Valid
│ │
▼ ▼
Login Required New JWT Issued
│
▼
User Continues
Να θυμάσαι!
Ένα σωστό authentication και authorization σύστημα δεν βασίζεται σε ένα μόνο layer, αλλά σε καθαρό διαχωρισμό ευθυνών.
Το middleware είναι το security gate που απορρίπτει invalid tokens πριν μπουν στο σύστημα.
Το authorization layer είναι το σημείο που αποφασίζει αν ένας χρήστης έχει πρόσβαση σε ένα συγκεκριμένο endpoint.
Το PermissionService είναι το σημείο που διαβάζει την πραγματική κατάσταση από τη βάση δεδομένων.
Και η cache είναι απλώς ένα performance layer που μειώνει το load χωρίς να επηρεάζει τη λογική του συστήματος.
Όταν όλα αυτά λειτουργούν μαζί με καθαρό διαχωρισμό, έχεις ένα σύστημα που είναι ταυτόχρονα ασφαλές, γρήγορο και έτοιμο για production scale χωρίς bottlenecks.

Top comments (0)