שרת משאבים OAuth 2.0 עם אבטחת אביב 5

1. סקירה כללית

במדריך זה נלמד כיצד להקים שרת משאבים OAuth 2.0 באמצעות Spring Security 5.

אנו נעשה זאת באמצעות JWT כמו גם אסימונים אטומים, שני סוגי אסימוני הנושא הנתמכים על ידי Spring Security.

לפני שנקפוץ לדוגמאות היישום והקוד, נקים רקע כלשהו.

2. רקע קטן

2.1. מהם JWTs ואסימונים אטומים?

JWT, או JSON Web Token הוא דרך להעביר מידע רגיש בצורה מאובטחת בפורמט JSON המקובל. המידע הכלול יכול להיות על המשתמש, או על האסימון עצמו, כגון תפוגתו ומנפיקו.

מצד שני, אסימון אטום, כפי שהשם מרמז, הוא אטום מבחינת המידע שהוא נושא. האסימון הוא רק מזהה המצביע על המידע המאוחסן בשרת ההרשאה - הוא מאומת באמצעות התבוננות פנימית בסוף השרת.

2.2. מהו שרת משאבים?

בהקשר של OAuth 2.0, א שרת משאבים הוא יישום המגן על משאבים באמצעות אסימוני OAuth. אסימונים אלה מונפקים על ידי שרת הרשאה, בדרך כלל ליישום לקוח. תפקידו של שרת המשאבים הוא לאמת את האסימון לפני הגשת משאב ללקוח.

תוקפו של אסימון נקבע על ידי כמה דברים:

  • האם אסימון זה הגיע משרת ההרשאה שהוגדר?
  • האם זה לא פג?
  • האם שרת משאבים זה קהל היעד שלו?
  • האם לאסימון יש את הסמכות הנדרשת לגשת למשאב המבוקש?

כדי להמחיש, בואו נסתכל על תרשים רצף עבור זרימת קוד ההרשאה ונראה את כל השחקנים בפעולה:

כפי שאנו יכולים לראות בשלב 8, כאשר יישום הלקוח מתקשר לממשק ה- API של שרת המשאבים לגשת למשאב מוגן, הוא עובר תחילה לשרת ההרשאה כדי לאמת את האסימון הכלול בבקשה. אישור: נושא כותרת ואז מגיב ללקוח.

שלב 9 הוא מה שאנו מתמקדים בו במדריך זה.

בסדר, עכשיו בואו נקפוץ לחלק הקוד. נקים שרת הרשאה באמצעות Keycloak, שרת משאבים המאמת אסימוני JWT, שרת משאבים אחר המאמת אסימונים אטומים, וכמה בדיקות JUnit כדי לדמות אפליקציות לקוח ולאמת תגובות.

3. שרת הרשאה

ראשית, נגדיר שרת הרשאות, או את הדבר שמנפיק אסימונים.

לשם כך, נשתמש ב- Keycloak המוטמע ביישום אתחול האביב. Keycloak הוא פיתרון קוד פתוח לניהול גישה וזהות. מכיוון שאנו מתמקדים בשרת המשאבים במדריך זה, לא נתעמק בו יותר.

לשרת Keycloak המשובץ שלנו מוגדרים שני לקוחות - fooClient ו barClient - המתאים לשני יישומי שרת המשאבים שלנו.

4. שרת משאבים - שימוש ב- JWT

לשרת המשאבים שלנו יהיו ארבעה מרכיבים עיקריים:

  • דֶגֶם - המשאב להגן
  • ממשק API - בקר REST לחשיפת המשאב
  • תצורת אבטחה - מחלקה להגדרת בקרת גישה למשאב המוגן אותו ה- API חושף
  • application.yml - קובץ תצורה להצהרת מאפיינים, כולל מידע אודות שרת ההרשאה

בואו נראה אותם בזה אחר זה עבור שרת המשאבים שלנו המטפל באסימני JWT, לאחר שהצצנו בתלות.

4.1. תלות Maven

בעיקר נצטרך את spring-boot-starter-oauth2-resource-server, המתנע של Spring Boot לתמיכה בשרת משאבים. המתנע הזה כולל Spring Security כברירת מחדל, כך שאיננו צריכים להוסיף אותו במפורש:

 org.springframework.boot spring-boot-starter-web 2.2.6.RELEASE org.springframework.boot spring-boot-starter-oauth2-resource-server 2.2.6.RELEASE org.apache.commons commons-lang3 3.9 

מלבד זאת, הוספנו גם תמיכה באינטרנט.

למטרות ההדגמה שלנו, ניצור משאבים באופן אקראי במקום לקבל אותם ממסד נתונים, עם קצת עזרה של אפאצ'י commons-lang3 סִפְרִיָה.

4.2. דֶגֶם

אם נשמור על זה פשוט, נשתמש פו, POJO, כמשאב המוגן שלנו:

מחלקה ציבורית Foo {מזהה פרטי ארוך; שם מחרוזת פרטי; // קונסטרוקטור, גטרים וקובעים} 

4.3. ממשק API

הנה בקר המנוחה שלנו, להכין פו זמין למניפולציה:

@RestController @RequestMapping (value = "/ foos") FooController בכיתה ציבורית {@GetMapping (value = "/ {id}") Foo findOne ציבורי (@PathVariable Long ID) {להחזיר Foo חדש (Long.parseLong (randomNumeric (2)) ), אקראי Alfabetisk (4)); } @GetMapping רשימה ציבורית findAll () {רשימה fooList = ArrayList חדש (); fooList.add (Foo חדש (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); fooList.add (Foo חדש (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); fooList.add (Foo חדש (Long.parseLong (randomNumeric (2)), randomAlphabetic (4))); החזר fooList; } @ResponseStatus (HttpStatus.CREATED) @PostMapping בטל ציבורי create (@RequestBody Foo newFoo) {logger.info ("Foo created"); }}

כפי שניכר, יש לנו את ההוראה לקבל הכל פוs, קבל א פו לפי מזהה, והודעה א פו.

4.4. תצורת אבטחה

במחלקת תצורה זו אנו מגדירים רמות גישה למשאב שלנו:

המחלקה הציבורית @Configuration JWTSecurityConfig מרחיב את WebSecurityConfigurerAdapter {@Override מוגן חלל להגדיר (HttpSecurity http) זורק חריג {http .authorizeRequests (authz -> authz .antMatchers (HttpMethod.GET, "/foos/**").hasAhor). .antMatchers (HttpMethod.POST, "/foos").hasAuthority("SCOPE_write") .anyRequest (). מאומת ()) .oauth2ResourceServer (oauth2 -> oauth2.jwt ()); }} 

כל מי שיש לו אסימון גישה שיש לו את לקרוא היקף יכול לקבל פוס. על מנת לפרסם חדש פו, אסימון שלהם צריך להיות לִכתוֹב תְחוּם.

בנוסף, הוספנו שיחה ל jwt () משתמש ב oauth2ResourceServer () DSL כדי לציין את סוג האסימונים הנתמכים על ידי השרת שלנו כאן.

4.5. application.yml

במאפייני היישום, בנוסף למספר היציאה הרגיל ונתיב ההקשר הרגיל, עלינו להגדיר את הנתיב ל- URI של מנפיק שרת ההרשאה שלנו כדי ששרת המשאבים יוכל לגלות את תצורת הספק שלו:

שרת: יציאה: 8081 servlet: path-context: / resource-server-jwt spring: security: oauth2: resourceserver: jwt: issuer-uri: // localhost: 8083 / auth / realms / baeldung

שרת המשאבים משתמש במידע זה כדי לאמת את אסימוני JWT המגיעים מיישום הלקוח, לפי שלב 9 בתרשים הרצפים שלנו.

כדי שאימות זה יעבוד באמצעות מנפיק-אורי מאפיין, שרת ההרשאה חייב להיות פעיל. אחרת, שרת המשאבים לא יופעל.

אם אנחנו צריכים להתחיל את זה באופן עצמאי, אנחנו יכולים לספק את jwk-set-uri במקום זאת כדי להצביע על נקודת הקצה של שרת ההרשאה החושפת מפתחות ציבוריים:

jwk-set-uri: // localhost: 8083 / auth / realms / baeldung / protocol / openid-connect / certs

וזה כל מה שאנחנו צריכים בכדי לגרום לשרת שלנו לאמת אסימונים של JWT.

4.6. בדיקה

לבדיקה, נקים JUnit. על מנת לבצע בדיקה זו, אנו זקוקים לשרת ההרשאות וכן לשרת המשאבים ולהפעלתו.

בואו נוודא שאנחנו יכולים להשיג פוs מ resource-server-jwt עם a לקרוא אסימון סקופ במבחן שלנו:

@Test ציבורי בטל givenUserWithReadScope_whenGetFooResource_thenSuccess () {String accessToken = obtainAccessToken ("read"); תגובת תגובה = RestAssured.given () .header (HttpHeaders.AUTHORIZATION, "Bearer" + accessToken) .get ("// localhost: 8081 / resource-server-jwt / foos"); assertThat (response.as (List.class)). hasSizeGreaterThan (0); }

בקוד לעיל, בשורה מס '3 אנו מקבלים אסימון גישה עם לקרוא היקף משרת ההרשאה, המכסה את השלבים 1 עד 7 בתרשים הרצפים שלנו.

שלב 8 מבוצע על ידי היה סמוך ובטוחשל לקבל() שִׂיחָה. שלב 9 מבוצע על ידי שרת המשאבים עם התצורות שראינו והוא שקוף לנו כמשתמשים.

5. שרת משאבים - שימוש באסימונים אטומים

לאחר מכן, בואו נראה את אותם הרכיבים עבור שרת המשאבים שלנו המטפל באסימונים אטומים.

5.1. תלות Maven

כדי לתמוך באסימונים אטומים, בנוסף נצטרך את oauth2-oidc-sdk תלות:

 com.nimbusds oauth2-oidc-sdk 8.19 זמן ריצה 

5.2. מודל ובקר

עבור זה, נוסיף a בָּר מַשׁאָב:

בר בכיתה ציבורית {מזהה פרטי ארוך; שם מחרוזת פרטי; // קונסטרוקטור, גטרים וקובעים} 

יהיה לנו גם BarController עם נקודות קצה דומות לשלנו FooController לפני, לדיווח בָּרס.

5.3. application.yml

בתוך ה application.yml כאן נצטרך להוסיף התבוננות פנימית-אורי המקביל לנקודת הסיום של שרת ההרשאה שלנו. כאמור, כך מאומת אסימון אטום:

server: port: 8082 servlet: path-path: / resource-server-opaque spring: security: oauth2: resourceserver: opaque: introspection-uri: // localhost: 8083 / auth / realms / baeldung / protocol / openid-connect / token / התבוננות פנימית-לקוח-מזהה: barClient מבט פנימי-לקוח-סוד: barClientSecret

5.4. תצורת אבטחה

שמירה על רמות גישה דומות לזו של פו בשביל ה בָּר גם משאב, גם מחלקת תצורה זו מתקשרת אל opaqueToken () משתמש ב oauth2ResourceServer () DSL כדי לציין את השימוש בסוג האסימון האטום:

@Configuration מחלקה ציבורית OpaqueSecurityConfig מרחיב את WebSecurityConfigurerAdapter {@Value ("$ {spring.security.oauth2.resourceserver.opaque.introspection-uri}") מחרוזת introspectionUri; @Value ("$ {spring.security.oauth2.resourceserver.opaque.introspection-client-id}") מחרוזת clientId; @Value ("$ {spring.security.oauth2.resourceserver.opaque.introspection-client-secret}") מחרוזת clientSecret; תצורת הריקות המוגנת על ידי @Override (HttpSecurity http) זורקת חריג {http .authorizeRequests (authz -> authz .antMatchers (HttpMethod.GET, "/bars/**").hasAuthority("SCOPE_read") .antMatchers (HttpMethod.POST, " / ברים ") .היה Autority(" SCOPE_write ") .anyRequest (). מאומת ()). oauth2ResourceServer (oauth2 -> oauth2 .opaqueToken (אסימון -> token.introspectionUri (this.introspectionUri) .introspectionClientCredentials זה. clientSecret))); }} 

כאן אנו מציינים גם את אישורי הלקוח המתאימים ללקוח שרת ההרשאה בו נשתמש. הגדרנו את אלה קודם לכן application.yml.

5.5. בדיקה

אנו מקימים JUnit עבור שרת המשאבים אטום מבוסס-האסימון שלנו, בדומה לאופן שעשינו זאת עבור ה- JWT.

במקרה זה, בואו לבדוק אם א לִכתוֹב אסימון גישה מקיף יכול לפרסם א בָּר ל משאבים-שרתים אטומים:

@Test הציבור בטל givenUserWithWriteScope_whenPostNewBarResource_thenCreated () {מחרוזת accessToken = להשיגAccessToken ("קרא לכתוב"); סרגל newBar = סרגל חדש (Long.parseLong (randomNumeric (2)), randomAlphabetic (4)); תגובת תגובה = RestAssured.given () .contentType (ContentType.JSON) .header (HttpHeaders.AUTHORIZATION, "Bearer" + accessToken) .body (newBar) .log () .all () .post ("// localhost: 8082 / resource-server-opaque / bars "); assertThat (response.getStatusCode ()). isEqualTo (HttpStatus.CREATED.value ()); }

אם נקבל סטטוס של CREATED בחזרה, המשמעות היא ששרת המשאבים אימת בהצלחה את האסימון האטום ויצר את בָּר בשבילנו.

6. מסקנה

במדריך זה ראינו כיצד להגדיר יישום שרת משאבים מבוסס Spring Security לאימות JWT כמו גם אסימונים אטומים.

כפי שראינו, עם הגדרה מינימלית, Spring איפשר לאמת בצורה חלקה את האסימונים עם מנפיק ולשלוח משאבים לצד המבקש - במקרה שלנו, מבחן JUnit.

כמו תמיד, קוד המקור זמין ב- GitHub.


$config[zx-auto] not found$config[zx-overlay] not found