מבוא לאבטחת שיטת אביב

1. הקדמה

במילים פשוטות, Spring Security תומך בסמנטיקה של הרשאות ברמת השיטה.

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

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

2. הפעלת אבטחת שיטות

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

 org.springframework.security spring-security-config 

אנו יכולים למצוא את הגרסה האחרונה שלה ב- Maven Central.

אם אנו רוצים להשתמש באביב אתחול, נוכל להשתמש ב- אביב-אתחול-התחלה-אבטחה תלות הכוללת אביב-ביטחון-תצורה:

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

שוב, ניתן למצוא את הגרסה האחרונה ב- Maven Central.

לאחר מכן, עלינו לאפשר אבטחת שיטות גלובלית:

@Configuration @EnableGlobalMethodSecurity (prePostEnabled = true, secureEnabled = true, jsr250Enabled = true) מחלקה ציבורית MethodSecurityConfig מרחיב את GlobalMethodSecurityConfiguration {}
  • ה prePostEnabled המאפיין מאפשר הערות לפני ואחרי ביטחון האביב
  • ה מאובטח המאפיין קובע אם ה- @מְאוּבטָח יש לאפשר הערה
  • ה jsr250 מופעל המאפיין מאפשר לנו להשתמש ב- @RoleAllowed ביאור

נחקור עוד על הערות אלה בחלק הבא.

3. יישום אבטחת שיטות

3.1. באמצעות @מְאוּבטָח ביאור

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

בואו נגדיר א getUsername שיטה:

@Secured ("ROLE_VIEWER") מחרוזת ציבורית getUsername () {SecurityContext securityContext = SecurityContextHolder.getContext (); להחזיר securityContext.getAuthentication (). getName (); }

הנה ה @ מאובטח ("ROLE_VIEWER") ביאור מגדיר שרק משתמשים שיש להם את התפקיד ROLE_VIEWER מסוגלים לבצע את getUsername שיטה.

חוץ מזה, אנו יכולים להגדיר רשימת תפקידים ב- @מְאוּבטָח ביאור:

@Secured ({"ROLE_VIEWER", "ROLE_EDITOR"}) בוליאני ציבורי isValidUsername (שם משתמש מחרוזת) {return userRoleRepository.isValidUsername (שם משתמש); }

במקרה זה, התצורה קובעת שאם למשתמש יש אחד מהם ROLE_VIEWER אוֹ ROLE_EDITORהמשתמש יכול להפעיל את isValidUsername שיטה.

ה @מְאוּבטָח ביאור אינו תומך בשפת ביטוי האביב (SpEL).

3.2. באמצעות @RoleAllowed ביאור

ה @RoleAllowed ההערה היא ההערה המקבילה של ה- JSR-250 @מְאוּבטָח ביאור.

בעיקרון, אנו יכולים להשתמש ב- @RoleAllowed ביאור באופן דומה לזה @מְאוּבטָח. לפיכך, נוכל להגדיר מחדש getUsername ו isValidUsername שיטות:

@RolesAllowed ("ROLE_VIEWER") מחרוזת ציבורית getUsername2 () {// ...} @RolesAllowed ({"ROLE_VIEWER", "ROLE_EDITOR"}) בוליאני ציבורי isValidUsername2 (שם משתמש מחרוזת) {// ...}

באופן דומה, רק המשתמש שיש לו תפקיד ROLE_VIEWER יכול לבצע getUsername2.

שוב, משתמש מסוגל להפעיל isValidUsername2 רק אם יש לה לפחות אחד מ ROLE_VIEWER אוֹ ROLER_EDITOR תפקידים.

3.3. באמצעות @ PreAuthorize ו @PostAuthorize ביאורים

שניהם @ PreAuthorize ו @PostAuthorize ביאורים מספקים בקרת גישה מבוססת ביטוי. מכאן שניתן לכתוב פרדיקטים באמצעות SpEL (שפת ביטוי האביב).

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

עכשיו, בואו נכריז על א getUsernameInUpperCase שיטה להלן:

@PreAuthorize ("hasRole ('ROLE_VIEWER')") ציבורי מחרוזת getUsernameInUpperCase () {להחזיר getUsername (). ToUpperCase (); }

ה @PreAuthorize ("hasRole ('ROLE_VIEWER')") יש את אותה משמעות כמו @ מאובטח ("ROLE_VIEWER") בו השתמשנו בחלק הקודם. אל תהסס לגלות פרטים נוספים על ביטויי אבטחה במאמרים קודמים.

כתוצאה מכך, ההערה @ מאובטח ({"ROLE_VIEWER", "ROLE_EDITOR"}) ניתן להחליף ב @PreAuthorize ("hasRole ('ROLE_VIEWER') או hasRole ('ROLE_EDITOR')"):

@PreAuthorize ("hasRole ('ROLE_VIEWER') או hasRole ('ROLE_EDITOR')") בוליאני ציבורי isValidUsername3 (שם משתמש מחרוזת) {// ...}

יתר על כך, אנו יכולים למעשה להשתמש בטיעון השיטה כחלק מהביטוי:

@PreAuthorize ("# username == authentication.principal.username") מחרוזת getMyRoles ציבורית (שם משתמש מחרוזת) {// ...}

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

כדאי לציין זאת @ PreAuthorize ניתן להחליף ביטויים ב @PostAuthorize יחידות.

בוא נשכתב getMyRoles:

@PostAuthorize ("# username == authentication.principal.username") ציבורי מחרוזת getMyRoles2 (שם משתמש מחרוזת) {// ...}

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

בנוסף, ה @PostAuthorize ביאור מספק אפשרות לגשת לתוצאת השיטה:

@PostAuthorize ("returnObject.username == authentication.principal.nickName") ציבורי CustomUser loadUserDetail (שם משתמש מחרוזת) {return userRoleRepository.loadUserByUserName (שם משתמש); }

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

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

3.4. באמצעות @לפני סינון ו @PostFilter ביאורים

אביב ביטחון מספק את @לפני סינון ביאור לסינון טיעון אוסף לפני ביצוע השיטה:

@PreFilter ("filterObject! = Authentication.principal.username") ציבורי מחרוזת להצטרף שמות משתמשים (שמות משתמשים ברשימה) {להחזיר usernames.stream (). Collect (Collectors.joining (";")); }

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

פה, בביטוי שלנו אנו משתמשים בשם filterObject לייצג את האובייקט הנוכחי באוסף.

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

@PreFilter (value = "filterObject! = Authentication.principal.username", filterTarget = "שמות משתמש") ציבורי מחרוזת joinUsernamesAndRoles (שמות משתמש ברשימה, תפקידי רשימה) {החזר usernames.stream (). Collect (Collectors.joining (";") ) + ":" + role.stream (). collect (Collectors.joining (";")); }

בנוסף, אנו יכולים גם לסנן את האוסף המוחזר של שיטה באמצעות @PostFilter ביאור:

@PostFilter ("filterObject! = Authentication.principal.username") רשימה ציבורית getAllUsernamesExceptCurrent () {return userRoleRepository.getAllUsernames (); }

במקרה זה, השם filterObject מתייחס לאובייקט הנוכחי באוסף המוחזר.

עם תצורה זו, Spring Security יחזור דרך הרשימה המוחזרת ויסיר כל ערך התואם לשם המשתמש של המנהל.

Spring Security - מאמר @PreFilter ו- @ PostFilter מתאר בפירוט רב יותר את שתי ההערות.

3.5. שיטה מטא-ביאור אבטחה

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

במקרה זה, אנו יכולים להגדיר מטה-ביאור אבטחה:

@Target (ElementType.METHOD) @Retention (RetentionPolicy.RUNTIME) @PreAuthorize ("hasRole ('VIEWER')") ציבורי @interface IsViewer {}

לאחר מכן, אנו יכולים להשתמש ישירות בהערת @IsViewer כדי להבטיח את השיטה שלנו:

מחרוזת ציבורית @IsViewer getUsername4 () {// ...}

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

3.6. ביאור ביטחוני ברמת הכיתה

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

@Service @PreAuthorize ("hasRole ('ROLE_ADMIN')") מחלקה ציבורית SystemService {public String getSystemYear () {// ...} String public getSystemDate () {// ...}}

בדוגמה לעיל, כלל האבטחה hasRole ('ROLE_ADMIN') יוחל על שניהם getSystemYear ו getSystemDate שיטות.

3.7. הערות אבטחה מרובות על שיטה

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

@PreAuthorize ("# username == authentication.principal.username") @ PostAuthorize ("returnObject.username == authentication.principal.nickName") ציבורי CustomUser secureLoadUserDetail (שם משתמש מחרוזת) {return userRoleRepository.loadUserByUserName (שם משתמש); }

לפיכך, אביב יאמת את ההרשאה גם לפני ביצוע הניהול secureLoadUserDetail שיטה.

4. שיקולים חשובים

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

  • כברירת מחדל, Proxying של Spring AOP משמש ליישום אבטחת שיטות - אם שיטה מאובטחת A נקראת בשיטה אחרת באותה מחלקה, האבטחה ב- A מתעלמת לחלוטין. משמעות הדבר היא ששיטה A תבוצע ללא בדיקת אבטחה. כך גם בשיטות פרטיות
  • אביב אבטחה הקשר קשורה בחוט - כברירת מחדל, הקשר האבטחה אינו מועבר לשרשורי ילדים. לקבלת מידע נוסף, אנו יכולים לעיין במאמר התפשטות הקשר של Spring Security

5. אבטחת שיטות בדיקה

5.1. תְצוּרָה

כדי לבדוק את אבטחת האביב עם JUnit, אנו זקוקים ל אביב-ביטחון-מבחן תלות:

 org.springframework.security אביב-אבטחה-מבחן 

איננו צריכים לציין את גרסת התלות מכיוון שאנו משתמשים בתוסף Spring Boot. אנו יכולים למצוא את הגרסה האחרונה של תלות זו ב- Maven Central.

לאחר מכן, בואו להגדיר מבחן פשוט של שילוב אביב על ידי ציון הרץ וה- ApplicationContext תְצוּרָה:

@RunWith (SpringRunner.class) @ContextConfiguration מחלקה ציבורית MethodSecurityIntegrationTest {// ...}

5.2. בדיקת שם משתמש ותפקידים

כעת, לאחר שהתצורה שלנו מוכנה, ננסה לבדוק את התצורה שלנו getUsername שיטה אותה אבטחנו באמצעות @ מאובטח ("ROLE_VIEWER") ביאור:

@Secured ("ROLE_VIEWER") מחרוזת ציבורית getUsername () {SecurityContext securityContext = SecurityContextHolder.getContext (); להחזיר securityContext.getAuthentication (). getName (); }

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

לָכֵן, עלינו לספק למשתמש לבדוק את השיטה המאובטחת שלנו. כדי להשיג זאת, אנו מקשטים את שיטת הבדיקה באמצעות @WithMockUser ולספק למשתמש ותפקידים:

@Test @WithMockUser (שם משתמש = "ג'ון", תפקידים = {"VIEWER"}) חלל ציבורי בהתחשב ב- RollViewer_whenCallGetUsername_thenReturnUsername () {String userName = userRoleService.getUsername (); assertEquals ("john", userName); }

סיפקנו משתמש מאומת ששמו המשתמש שלו הוא ג'ון ותפקידם ROLE_VIEWER. אם לא נציין את שם משתמש אוֹ תַפְקִיד, ברירת המחדל שם משתמש הוא מִשׁתַמֵשׁ וברירת מחדל תַפְקִיד הוא ROLE_USER.

שים לב שאין צורך להוסיף את ה- תַפְקִיד_ קידומת כאן, Spring Security יוסיף קידומת זו באופן אוטומטי.

אם אנחנו לא רוצים לקבל את הקידומת הזו, נוכל לשקול להשתמש רָשׁוּת במקום תַפְקִיד.

לדוגמה, בואו נכריז על א getUsernameInLowerCase שיטה:

@PreAuthorize ("hasAuthority ('SYS_ADMIN')") ציבורי מחרוזת getUsernameLC () {return getUsername (). ToLowerCase (); }

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

@Test @WithMockUser (שם משתמש = "JOHN", רשויות = {"SYS_ADMIN"}) חלל ציבורי givenAuthoritySysAdmin_whenCallGetUsernameLC_thenReturnUsername () {String username = userRoleService.getUsernameInLowerCase (); assertEquals ("ג'ון", שם משתמש); }

בנוחות, אם אנו רוצים להשתמש באותו משתמש במקרי בדיקה רבים, אנו יכולים להכריז על @WithMockUser ביאור בשיעור המבחן:

@RunWith (SpringRunner.class) @ContextConfiguration @WithMockUser (username = "john", role = {"VIEWER"}) מחלקה ציבורית MockUserAtClassLevelIntegrationTest {// ...}

אם היינו רוצים להריץ את הבדיקה שלנו כמשתמש אנונימי, נוכל להשתמש ב- @WithAnonymousUser ביאור:

@Test (צפוי = AccessDeniedException.class) @WithAnonymousUser ציבורי בטל givenAnomynousUser_whenCallGetUsername_thenAccessDenied () {userRoleService.getUsername (); }

בדוגמה לעיל אנו מצפים ל- AccessDeniedException כי המשתמש האנונימי אינו מקבל את התפקיד ROLE_VIEWER או הסמכות SYS_ADMIN.

5.3. בדיקה בהתאמה אישית UserDetailsService

עבור רוב היישומים, מקובל להשתמש בכיתה מותאמת אישית כעקרון אימות. במקרה זה, המחלקה המותאמת אישית צריכה ליישם את ה- org.springframework.security.core.userdetails.פרטי המשתמש מִמְשָׁק.

במאמר זה אנו מצהירים כי א CustomUser מחלקה המרחיבה את היישום הקיים של פרטי המשתמש, שזה org.springframework.security.core.userdetails.מִשׁתַמֵשׁ:

מחלקה ציבורית CustomUser מרחיב את המשתמש {private String nickname; // גטר וקובע}

בואו נחזיר את הדוגמא עם ה- @PostAuthorize ביאור בסעיף 3:

@PostAuthorize ("returnObject.username == authentication.principal.nickName") ציבורי CustomUser loadUserDetail (שם משתמש מחרוזת) {return userRoleRepository.loadUserByUserName (שם משתמש); }

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

אם היינו רוצים לבדוק את השיטה הזונוכל לספק יישום של UserDetailsService שיכולים להעמיס את שלנו CustomUser בהתבסס על שם המשתמש:

@Test @WithUserDetails (value = "john", userDetailsServiceBeanName = "userDetailService") בטל פומבי כאשר John_callLoadUserDetail_thenOK () {CustomUser user = userService.loadUserDetail ("jane"); assertEquals ("jane", user.getNickName ()); }

הנה ה @WithUserDetails ההערה קובעת שנשתמש ב- UserDetailsService לאתחל את המשתמש המאומת שלנו. השירות מופנה על ידי userDetailsServiceBeanName תכונה. זֶה UserDetailsService יכול להיות יישום אמיתי או זיוף למטרות בדיקה.

בנוסף, השירות ישתמש בערך הנכס ערך כשם המשתמש לטעינה פרטי המשתמש.

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

5.4. בדיקה עם הערות מטא

לעתים קרובות אנו מוצאים את עצמנו משתמשים שוב ושוב באותו משתמש / תפקידים במבחנים שונים.

במצבים אלה, נוח ליצור א מטה-ביאור.

אם נחזיר את הדוגמה הקודמת @WithMockUser (שם משתמש = "john", תפקידים = {"VIEWER"}), אנו יכולים להכריז על מטה-ביאור כ:

@Retention (RetentionPolicy.RUNTIME) @WithMockUser (value = "john", role = "VIEWER") ממשק ציבורי @ WithMockJohnViewer {}

אז אנחנו יכולים פשוט להשתמש @WithMockJohnViewer במבחן שלנו:

@Test @WithMockJohnViewer חלל ציבורי givenMockedJohnViewer_whenCallGetUsername_thenReturnUsername () {String userName = userRoleService.getUsername (); assertEquals ("john", userName); }

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

6. מסקנה

במדריך זה בחנו אפשרויות שונות לשימוש באבטחת שיטות באבטחת אביב.

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

כל הדוגמאות של מדריך זה ניתן למצוא באתר Github.


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