ענן אביב - שירותי אבטחה

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

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

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

שיתופי הפעלות נותנים לנו את היכולת לרשום משתמשים בשירות השער שלנו ולהפיץ את האימות הזה לכל שירות אחר במערכת שלנו.

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

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

2. הגדרת Maven

נתחיל בהוספת תלות האביב-אתחול-המתנע-אבטחה לכל מודול במערכת:

 org.springframework.boot spring-boot-starter-security 

כי אנחנו משתמשים אביב ניהול תלות שנוכל להשמיט עבורו את הגרסאות אביב-אתחול-מתנע תלות.

כשלב שני, בואו לשנות את ה- pom.xml של כל יישום עם תלות באביב, באתחול-אתחול-נתונים-רדיס:

 org.springframework.session spring-session org.springframework.boot spring-boot-starter-data-redis 

רק ארבע מהיישומים שלנו יתקשרו מושב אביב: תַגלִית, כְּנִיסָה, שירות ספרים, ו שירות דירוג.

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

@EnableRedisHttpSession מחלקה ציבורית SessionConfig מרחיב תקציר HttpSessionApplicationInitializer {}

אחרון, הוסף את המאפיינים הללו לשלוש *.נכסים קבצים במאגר git שלנו:

spring.redis.host = localhost spring.redis.port = 6379

עכשיו בואו נקפוץ לתצורה ספציפית לשירות.

3. אבטחת שירות תצורה

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

תן לנו להוסיף מאפייני אבטחה ל- application.properties קובץ src / main / resources של שירות התצורה:

eureka.client.serviceUrl.defaultZone = // discUser: [email protected]: 8082 / eureka / security.user.name = configUser security.user.password = configPassword security.user.role = SYSTEM

זה יגדיר את השירות שלנו להתחבר עם גילוי. בנוסף, אנו מגדירים את האבטחה שלנו עם ה- application.properties קוֹבֶץ.

בואו כעת להגדיר את שירות הגילוי שלנו.

4. אבטחת שירות גילוי

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

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

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

בואו נוסיף מסנן אבטחה כדי להגן על נקודות הקצה בהן ישתמשו השירותים האחרים:

@Configuration @EnableWebSecurity @Order (1) מחלקה ציבורית SecurityConfig מרחיב את WebSecurityConfigurerAdapter {@Autowired public void configureGlobal (AuthenticationManagerBuilder auth) {auth.inMemoryAuthentication (). WithUser ("discUser"). סיסמת ("). ); } הגדרת חלל מוגן @Override (HttpSecurity http) {http.sessionManagement () .sessionCreationPolicy (SessionCreationPolicy.ALWAYS) .and (). RequestMatchers (). AntMatchers ("/ eureka / **"). And (). AuthorizeRequests () .antMatchers ("/ eureka / **") .hasRole ("SYSTEM"). anyRequest (). denyAll (). ו- () .httpBasic (). ו- (). csrf (). השבת (); }}

זה יגדיר את השירות שלנו עם 'מערכת'משתמש. זה בסיסי אביב ביטחון תצורה עם כמה פיתולים. בואו נסתכל על הפיתולים האלה:

  • הזמנה @ (1) - מספר אביב לחבר תחילה את מסנן האבטחה הזה כדי שהוא ינסה לפני כל האחרים
  • .sessionCreationPolicy - מספר אביב ליצור תמיד מושב כאשר משתמש נכנס למסנן זה
  • .requestMatchers - מגביל את נקודות הקצה שמסנן זה חל עליהן

מסנן האבטחה, שהקמנו זה עתה, מגדיר סביבת אימות מבודדת הנוגעת לשירות הגילוי בלבד.

4.2. אבטחת לוח המחוונים של יוריקה

מכיוון שליישום הגילוי שלנו יש ממשק משתמש נחמד לצפייה בשירותים הרשומים כעת, בואו נחשוף את השימוש במסנן אבטחה שני ונקשר את זה לאימות עבור שאר היישום. יש לזכור שלא @להזמין() תג פירושו שמדובר במסנן האבטחה האחרון שהוערך:

@Configuration מחלקה סטטית ציבורית AdminSecurityConfig מרחיב את WebSecurityConfigurerAdapter {@Override מוגן ריק להגדיר (HttpSecurity http) {http.sessionManagement (). SessionCreationPolicy (SessionCreationPolicy.NEVER). וגם (). HttpBasic (). Disable (). HttpMethod.GET, "/").hasRole("ADMIN") .antMatchers ("/ info", "/ health"). מאומת (). AnyRequest () .denyAll (). ו- (). Csrf (). השבת (); }}

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

  • httpBasic (). השבת () - אומר לאבטחת האביב להשבית את כל הליכי האימות של המסנן הזה
  • sessionCreationPolicy הגדרנו את זה לעולם לא כדי לציין שאנחנו דורשים מהמשתמש כבר אימות לפני הגישה למשאבים המוגנים על ידי מסנן זה

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

4.3. אימות באמצעות שירות התצורה

בפרויקט הגילוי, בואו נצמיד שני מאפיינים ל- bootstrap.properties ב- src / main / resources:

spring.cloud.config.username = config משתמש spring.cloud.config.password = configPassword

מאפיינים אלה יאפשרו לשירות הגילוי להיות מאומת עם שירות התצורה בעת ההפעלה.

בואו נעדכן את שלנו discovery.properties במאגר Git שלנו

eureka.client.serviceUrl.defaultZone = // discUser: [email protected]: 8082 / eureka / eureka.client.register-with-eureka = false eureka.client.fetch-registry = false

הוספנו אישורי אימות בסיסיים שלנו תַגלִית שירות כדי לאפשר לו לתקשר עם config שֵׁרוּת. בנוסף, אנו מגדירים יוריקה לרוץ במצב עצמאי על ידי כך שנאמר לשירות שלנו לא להירשם לעצמו.

בואו נתחייב את הקובץ ל- git מאגר. אחרת, השינויים לא יתגלו.

5. אבטחת שירות שער

שירות השער שלנו הוא החלק היחיד ביישום שאנחנו רוצים לחשוף לעולם. ככזה הוא יזדקק לאבטחה כדי להבטיח שרק משתמשים מאומתים יוכלו לגשת למידע רגיש.

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

בואו ניצור אבטחהקונפיג מחלקה כמו שירות הגילוי שלנו ומחליפים את השיטות בתוכן זה:

@Autourired public void configureGlobal (AuthenticationManagerBuilder auth) {auth.inMemoryAuthentication (). WithUser ("user"). Password ("password") .roles ("USER"). And (). WithUser ("admin"). סיסמא ( "admin") .roles ("ADMIN"); } תצורת הריקות המוגנת על ידי @Override (HttpSecurity http) {http.authorizeRequests (). AntMatchers ("/ book-service / books") .permitAll (). AntMatchers ("/ eureka / **"). HasRole ("ADMIN") .anyRequest (). מאומת (). ו- (). formLogin (). ו- () .logout (). permAll (). logoutSuccessUrl ("/ שירות שירות / ספרים") .permitAll (). ו- (). csrf (). disable (); }

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

הביטחון ב- / eureka / ** הוא הגנה על כמה משאבים סטטיים שנשרת משירות השער שלנו עבור יוריקה דף סטטוס. אם אתה בונה את הפרויקט עם המאמר, העתק את משאב / סטטי תיקייה מפרויקט השער ב- Github לפרויקט שלך.

כעת אנו משנים את ה- @EnableRedisHttpSession הערה לשיעור התצורה שלנו:

@EnableRedisHttpSession (redisFlushMode = RedisFlushMode.IMMEDIATE)

הגדרנו את מצב ההדחה באופן מיידי כדי להמשיך בכל שינוי במפגש באופן מיידי. זה עוזר בהכנת אסימון האימות להפניה מחדש.

לסיום, בואו נוסיף a ZuulFilter שיעביר את אסימון האימות שלנו לאחר ההתחברות:

@SessionSavingZuulPreFilter מחלקה ציבורית ברכיב ציבורי מרחיב את ZuulFilter {@ מאגר פרטי SessionRepository פרטי; @ Override בוליאני ציבורי shouldFilter () {להחזיר נכון; } @Override הפעלת אובייקט ציבורי () {RequestContext context = RequestContext.getCurrentContext (); HttpSession httpSession = context.getRequest (). GetSession (); מושב מושב = repository.getSession (httpSession.getId ()); context.addZuulRequestHeader ("עוגיה", "SESSION =" + httpSession.getId ()); החזר אפס; } @Override ציבור מחרוזת filterType () {להחזיר "pre"; } @Override int filter public Filter () {להחזיר 0; }}

מסנן זה יתפוס את הבקשה כאשר היא מנותבת לאחר הכניסה ויוסיף את מפתח ההפעלה כקובץ cookie בכותרת. זה יפיץ אימות לכל שירות גיבוי לאחר הכניסה.

5.2. אימות באמצעות שירות תצורה וגילוי

בואו נוסיף את מאפייני האימות הבאים ל- bootstrap.properties קובץ src / main / resources של שירות השער:

spring.cloud.config.username = configUser spring.cloud.config.password = configPassword eureka.client.serviceUrl.defaultZone = // discUser: [דוא"ל מוגן]: 8082 / eureka /

לאחר מכן, בואו נעדכן את שלנו gateway.properties במאגר Git שלנו

management.security.sessions = תמיד zuul.routes.book-service.path = / book-service / ** zuul.routes.book-service.sensitive-headers = Set-Cookie, Authorization hystrix.command.book-service.execution .isolation.thread .timeoutInMilliseconds = 600000 zuul.routes.rating-service.path = / rating-service / ** zuul.routes.rating-service.sensitive-headers = Set-Cookie, Authorization hystrix.command.rating-service. execution.isolation.thread .timeoutInMilliseconds = 600000 zuul.routes.discovery.path = / discovery / ** zuul.routes.discovery.sensitive-headers = Set-Cookie, הרשאה zuul.routes.discovery.url = // localhost: 8082 hystrix.command.discovery.execution.isolation.thread .timeoutInMilliseconds = 600000

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

בנוסף, הוספנו מסלול שיפנה בקשות לשירות הגילוי שלנו. מכיוון ששירות גילוי עצמאי לא יירשם עם עצמו, עלינו לאתר את השירות באמצעות תוכנית URL.

אנחנו יכולים להסיר את serviceUrl.defaultZone נכס מה gateway.properties הקובץ במאגר git התצורה שלנו. ערך זה משוכפל ב bootstrap קוֹבֶץ.

בואו נחייב את הקובץ למאגר Git, אחרת השינויים לא יתגלו.

6. אבטחת שירות ספרים

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

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

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

תצורת הריקות המוגנת על ידי @Override (HttpSecurity http) {http.httpBasic (). השבתה (). AuthorizeRequests () .antMatchers ("/ books"). PermAll () .antMatchers ("/ books / *"). HasAnyRole ("USER "," ADMIN "). מאומת (). ו- (). Csrf (). השבת (); }

6.2. נכסים

הוסף מאפיינים אלה ל- bootstrap.properties קובץ src / main / resources של שירות הספרים:

spring.cloud.config.username = configUser spring.cloud.config.password = configPassword eureka.client.serviceUrl.defaultZone = // discUser: [email protected]: 8082 / eureka /

בואו נוסיף מאפיינים שלנו book-service.properties הקובץ במאגר ה- git שלנו:

management.security.sessions = לעולם לא

אנחנו יכולים להסיר את serviceUrl.defaultZone נכס מה book-service.properties הקובץ במאגר git התצורה שלנו. ערך זה משוכפל ב bootstrap קוֹבֶץ.

זכור לבצע שינויים אלה כך ששירות הספרים יאסוף אותם.

7. אבטחת שירות דירוג

גם שירות הדירוג צריך להיות מאובטח.

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

כדי להבטיח את שירות הדירוג שלנו נעתיק את אבטחהקונפיג בכיתה מהשער ודרוס את השיטה בתוכן זה:

תצורת הריקות המוגנת על ידי @Override (HttpSecurity http) {http.httpBasic (). השבת (). AuthorizeRequests () .antMatchers ("/ ratings"). HasRole ("USER") .antMatchers ("/ ratings / all"). HasAnyRole ("USER", "ADMIN"). AnyRequest (). מאומת (). ו- (). Csrf (). השבת (); }

אנחנו יכולים למחוק את configureGlobal () שיטה מה- כְּנִיסָה שֵׁרוּת.

7.2. נכסים

הוסף מאפיינים אלה ל- bootstrap.properties קובץ src / main / resources של שירות הדירוג:

spring.cloud.config.username = configUser spring.cloud.config.password = configPassword eureka.client.serviceUrl.defaultZone = // discUser: [דוא"ל מוגן]: 8082 / eureka /

בואו נוסיף נכסים לשירות הדירוג שלנו.נכסים הקובץ במאגר ה- git שלנו:

management.security.sessions = לעולם לא

אנחנו יכולים להסיר את serviceUrl.defaultZone נכס משירות הדירוג.נכסים הקובץ במאגר git התצורה שלנו. ערך זה משוכפל ב bootstrap קוֹבֶץ.

זכור לבצע שינויים אלה כך ששירות הדירוג יאסוף אותם.

8. ריצה ובדיקה

הַתחָלָה רדיס וכל השירותים ליישום: תצורה, גילוי, שער, שירות ספרים, ו שירות דירוג. עכשיו בואו לבדוק!

ראשית, בואו ליצור שיעור מבחן אצלנו כְּנִיסָה פרויקט ויצירת שיטה למבחן שלנו:

מחלקה ציבורית GatewayApplicationLiveTest {@Test public void testAccess () {...}}

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

TestRestTemplate testRestTemplate = TestRestTemplate חדש (); מחרוזת testUrl = "// localhost: 8080"; תגובה ResponseEntity = testRestTemplate .getForEntity (testUrl + "/ book-service / books", String.class); Assert.assertEquals (HttpStatus.OK, response.getStatusCode ()); Assert.assertNotNull (response.getBody ());

הפעל בדיקה זו וודא את התוצאות. אם אנו רואים כשלים מאשרים כי היישום כולו התחיל בהצלחה וכי התצורות נטענו ממאגר git התצורה שלנו.

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

response = testRestTemplate .getForEntity (testUrl + "/ book-service / books / 1", String.class); Assert.assertEquals (HttpStatus.FOUND, response.getStatusCode ()); Assert.assertEquals ("// localhost: 8080 / login", response.getHeaders () .get ("מיקום"). Get (0));

הפעל את הבדיקה שוב וודא שהיא מצליחה.

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

טופס MultiValueMap = חדש LinkedMultiValueMap (); form.add ("שם משתמש", "משתמש"); form.add ("סיסמה", "סיסמה"); תגובה = testRestTemplate .postForEntity (testUrl + "/ login", טופס, String.class); 

עכשיו, בואו נחלץ את ההפעלה מהעוגיה ונפיץ אותה לבקשה הבאה:

מחרוזת sessionCookie = response.getHeaders (). Get ("Set-Cookie") .get (0) .split (";") [0]; כותרות HttpHeaders = HttpHeaders חדשות (); headers.add ("עוגיה", sessionCookie); HttpEntity httpEntity = HttpEntity חדש (כותרות); 

ובקש את המשאב המוגן:

תגובה = testRestTemplate.exchange (testUrl + "/ book-service / books / 1", HttpMethod.GET, httpEntity, String.class); Assert.assertEquals (HttpStatus.OK, response.getStatusCode ()); Assert.assertNotNull (response.getBody ());

הפעל את הבדיקה שוב כדי לאשר את התוצאות.

עכשיו, ננסה לגשת לקטע הניהול באותה הפעלה:

תגובה = testRestTemplate.exchange (testUrl + "/ service-service / ratings / all", HttpMethod.GET, httpEntity, String.class); Assert.assertEquals (HttpStatus.FORBIDDEN, response.getStatusCode ());

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

הבדיקה הבאה תאמת שנוכל להתחבר כמנהל ולגשת למשאב המוגן על ידי מנהל המערכת:

form.clear (); form.add ("שם משתמש", "מנהל"); form.add ("סיסמה", "admin"); תגובה = testRestTemplate .postForEntity (testUrl + "/ login", טופס, String.class); sessionCookie = response.getHeaders (). get ("Set-Cookie"). get (0) .split (";") [0]; כותרות = HttpHeaders חדשים (); headers.add ("עוגיה", sessionCookie); httpEntity = HttpEntity חדש (כותרות); תגובה = testRestTemplate.exchange (testUrl + "/ service-service / ratings / all", HttpMethod.GET, httpEntity, String.class); Assert.assertEquals (HttpStatus.OK, response.getStatusCode ()); Assert.assertNotNull (response.getBody ());

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

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

תגובה = testRestTemplate.exchange (testUrl + "/ גילוי", HttpMethod.GET, httpEntity, String.class); Assert.assertEquals (HttpStatus.OK, response.getStatusCode ());

הפעל בדיקה זו בפעם האחרונה כדי לאשר שהכל עובד. הַצלָחָה!!!

התגעגעת לזה? מכיוון שנכנסנו לשירות השער שלנו וצפינו בתכנים בשירותי הספר, הדירוג והגילוי שלנו מבלי שנצטרך להתחבר לארבעה שרתים נפרדים!

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

9. מסקנה

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

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

כמו תמיד תוכלו למצוא את קוד המקור ב- GitHub.


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