מבוא ל- ACL באביב

1. הקדמה

גש לרשימת השליטה (ACL) היא רשימה של הרשאות המצורפות לאובייקט. An ACL מציין אילו זהויות ניתנות אילו פעולות על אובייקט נתון.

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

לדוגמא, משתמש בעל התפקיד מנהל יכול לראות (לקרוא) ולערוך (לִכתוֹב) כל ההודעות על תיבת הודעות מרכזית, אך המשתמש הרגיל יכול רק לראות הודעות, להתייחס אליהם ולא יכול לערוך. בינתיים, משתמשים אחרים משתמשים בתפקיד עוֹרֵך יכול לראות ולערוך כמה הודעות ספציפיות.

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

2. תצורה

2.1. מאגר ACL

להשתמש אביב אבטחה ACL, עלינו ליצור ארבע טבלאות חובה במסד הנתונים שלנו.

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

  • תְעוּדַת זֶהוּת
  • מעמד: שם המחלקה של אובייקטים מתחום מאובטח, למשל:com.baeldung.acl.persistence.entity.NoticeMessage

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

  • תְעוּדַת זֶהוּת
  • SID: שהוא שם המשתמש או שם התפקיד. SID מייצג זהות ביטחונית
  • קֶרֶן: 0 אוֹ 1, כדי לציין כי המקביל SID הוא מנהל (משתמש, כגון מרי, מייק, ג'ק ...) או רשות (תפקיד, כגון ROLE_ADMIN, ROLE_USER, ROLE_EDITOR ...)

השולחן הבא הוא ACL_OBJECT_IDENTITY, המאחסן מידע לכל אובייקט תחום ייחודי:

  • תְעוּדַת זֶהוּת
  • OBJECT_ID_CLASS: להגדיר את מחלקת אובייקט התחום,קישורים אל ACL_CLASS שולחן
  • OBJECT_ID_IDENTITY: ניתן לאחסן אובייקטים של דומיינים בטבלאות רבות, תלוי בכיתה. לפיכך, שדה זה מאחסן את המפתח הראשי של אובייקט היעד
  • PARENT_OBJECT: ציין הורה לזה זהות אובייקט בתוך הטבלה הזו
  • OWNER_SID: תְעוּדַת זֶהוּת של בעל האובייקט, קישורים אל ACL_SID שולחן
  • ENTRIES_INHERITTING: האם ערכי ACL של אובייקט זה יורש מהאובייקט האב (ערכי ACL מוגדרים ב ACL_ENTRY שולחן)

סוף - סוף, ה ACL_ENTRY לאחסן הקצאת הרשאות אישיות לכל אחת SID על זהות אובייקט:

  • תְעוּדַת זֶהוּת
  • ACL_OBJECT_IDENTITY: ציין את זהות האובייקט, קישורים אל ACL_OBJECT_IDENTITY שולחן
  • ACE_ORDER: סדר הכניסה הנוכחית ב ערכי ACL רשימת המקבילים זהות אובייקט
  • SID: המטרה SID שההיתר ניתן או נדחה ממנו, קישורים אליו ACL_SID שולחן
  • מסכה: מסכת הסיביות השלמות המייצגת את ההרשאה בפועל הניתנת או נדחית
  • מענק: ערך 1 פירושו הענקה, ערך 0 פירושו להכחיש
  • AUDIT_SUCCESS ו AUDIT_FAILURE: למטרת ביקורת

2.2. תלות

להיות מסוגל להשתמש אביב ACL בפרויקט שלנו, בואו נגדיר תחילה את התלות שלנו:

 org.springframework.security spring-security-acl org.springframework.security spring-security-config org.springframework spring-context-support net.sf.ehcache ehcache-core 2.6.11 

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

כשלא עובדים עם Spring Boot, עלינו להוסיף גרסאות במפורש. ניתן לבדוק את אלה ב- Maven Central: spring-security-acl, spring-security-config, spring-context-support, ehcache-core.

2.3. תצורה הקשורה ל- ACL

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

@Configuration @EnableGlobalMethodSecurity (prePostEnabled = true, secureEnabled = true) מחלקה ציבורית AclMethodSecurityConfiguration מרחיב את GlobalMethodSecurityConfiguration {@Autowired MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler; @Override מוגן MethodSecurityExpressionHandler createExpressionHandler () {להחזיר defaultMethodSecurityExpressionHandler; }}

בואו גם נפעיל בקרת גישה מבוססת ביטוי על פי הגדרה prePostEnabled ל נָכוֹן להשתמש שפת ביטוי האביב (SpEL). יתר על כך, אנחנו צריכים מטפל בהבעה עם ACL תמיכה:

@Bean הציבור MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler () {DefaultMethodSecurityExpressionHandler expressionHandler = חדש DefaultMethodSecurityExpressionHandler (); AclPermissionEvaluator permissionEvaluator = AclPermissionEvaluator חדש (aclService ()); expressionHandler.setPermissionEvaluator (permitEvaluator); ביטוי להחזיר מפעיל; }

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

לשם פשטות, אנו משתמשים בתנאי השירות JdbcMutableAclService:

@Bean JdbcMutableAclService ציבוריים aclService () {להחזיר JdbcMutableAclService חדש (dataSource, lookupStrategy (), aclCache ()); }

כשמו, ה JdbcMutableAclService שימושים JDBCTemplate כדי לפשט את הגישה למסד הנתונים. זה צריך a מקור מידע (ל JDBCTemplate), LookupStrategy (מספק בדיקת אופטימיזציה בעת שאילתת מסד נתונים), ו- AclCache (מטמון ACLערכים ו זהות אובייקט).

שוב, לפשטות, אנו משתמשים בתנאי BasicLookupStrategy ו EhCacheBasedAclCache.

@ Source DataSource האוטומטי; @Bean AclAuthorizationStrategy public aclAuthorizationStrategy () {להחזיר AclAuthorizationStrategyImpl חדש (SimpleGrantedAuthority חדש ("ROLE_ADMIN")); } @ שעועית ציבורית PermissionGrantingStrategy הרשאהGrantingStrategy () {להחזיר DefaultPermissionGrantingStrategy חדש (ConsoleAuditLogger חדש ()); } @Bean ציבורי EhCacheBasedAclCache aclCache () {החזר EhCacheBasedAclCache חדש (aclEhCacheFactoryBean (). GetObject (), permitGrantingStrategy (), aclAuthorizationStrategy ()); } @Bean ציבור EhCacheFactoryBean aclEhCacheFactoryBean () {EhCacheFactoryBean ehCacheFactoryBean = חדש EhCacheFactoryBean (); ehCacheFactoryBean.setCacheManager (aclCacheManager (). getObject ()); ehCacheFactoryBean.setCacheName ("aclCache"); להחזיר ehCacheFactoryBean; } @Bean הציבור EhCacheManagerFactoryBean aclCacheManager () {להחזיר EhCacheManagerFactoryBean חדש (); } @Bean LookupStrategy public lookupStrategy () {להחזיר BasicLookupStrategy חדש (dataSource, aclCache (), aclAuthorizationStrategy (), ConsoleAuditLogger () חדש; } 

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

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

3. אבטחת שיטה עם ACL באביב

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

כברירת מחדל, אביב ACL מתייחס ל BasePermission בכיתה לכל ההרשאות הזמינות. בעיקרון, יש לנו קרא, כתוב, צור, מחק ו מִנהָל רְשׁוּת.

בואו ננסה להגדיר כמה כללי אבטחה:

@PostFilter ("hasPermission (filterObject, 'READ')") רשימה findAll (); @PostAuthorize ("hasPermission (returnObject, 'READ')") NoticeMessage findById (מזהה שלם); @PreAuthorize ("hasPermission (#noticeMessage, 'WRITE')") NoticeMessage save (@Param ("noticeMessage") NoticeMessage noticeMessage);

לאחר הביצוע של מצא הכל() שיטה, @PostFilter יופעל. הכלל הנדרש hasPermission (filterObject, 'READ'), פירושו להחזיר רק את אלה NoticeMessage איזה משתמש יש לקרוא הרשאה ב-.

בדומה לכך, @PostAuthorize מופעלת לאחר הביצוע של findById () שיטה, ודא רק להחזיר את NoticeMessage התנגד אם למשתמש הנוכחי יש לקרוא אישור עליו. אם לא, המערכת תזרוק AccessDeniedException.

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

4. בפעולה

עכשיו נבדוק את כל התצורות האלה באמצעות JUnit. נשתמש H2 מסד נתונים כדי לשמור על תצורה פשוטה ככל האפשר.

נצטרך להוסיף:

 com.h2database h2 org.springframework spring-test test org.springframework.security spring-security-test test 

4.1. התרחיש

בתרחיש זה יהיו לנו שני משתמשים (מנהל, hr) ותפקיד משתמש אחד (ROLE_EDITOR), אז שלנו acl_sid יהיה:

INSERT INTO acl_sid (id, principal, sid) VALUES (1, 1, 'manager'), (2, 1, 'hr'), (3, 0, 'ROLE_EDITOR');

ואז, אנחנו צריכים להכריז NoticeMessage כיתה ב acl_class. ושלושה מקרים של NoticeMessage הכיתה תוכנס ל הודעת מערכת.

יתר על כן, יש להכריז על רשומות תואמות עבור אותם 3 מקרים acl_object_identity:

הכנס ל- acl_class (id, class) VALUES (1, 'com.baeldung.acl.persistence.entity.NoticeMessage'); INSERT INTO system_message (id, content) VALUES (1, 'Message Level First'), (2, 'Message Level Second'), (3, 'Message Level Third'); הכנס ל- acl_object_identity (id, object_id_class, object_id_identity, parent_object, owner_sid, entries_inheriting) VALUES (1, 1, 1, NULL, 3, 0), (2, 1, 2, NULL, 3, 0), (3, 1 3, NULL, 3, 0);

בתחילה, אנו מעניקים לקרוא ו לִכתוֹב הרשאות על האובייקט הראשון (id = 1) למשתמש מנהל. בינתיים, כל משתמש עם ROLE_EDITOR יהיה לקרוא אישור על כל שלושת האובייקטים אך רק בעלי לִכתוֹב אישור על האובייקט השלישי (id = 3). חוץ מזה, משתמש שעה יהיה רק לקרוא אישור על האובייקט השני.

הנה, כי אנו משתמשים בברירת מחדל אביב ACLBasePermission בכיתה לבדיקת הרשאות, ערך המסכה של לקרוא ההרשאה תהיה 1, וערך המסכה של לִכתוֹב האישור יהיה 2. הנתונים שלנו ב acl_entry יהיה:

INSERT INTO acl_entry (id, acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure) VALUES (1, 1, 1, 1, 1, 1, 1, 1, 1), (2, 1, 2, 1, 2, 1, 1, 1), (3, 1, 3, 3, 1, 1, 1, 1), (4, 2, 1, 2, 1, 1, 1, 1), (5, 2, 2, 3, 1, 1, 1, 1), (6, 3, 1, 3, 1, 1, 1, 1), (7, 3, 2, 3, 2, 1, 1, 1);

4.2. מקרה מבחן

קודם כל, אנחנו מנסים לקרוא ל מצא הכל שיטה.

כתצורה שלנו, השיטה מחזירה רק את אלה NoticeMessage עליו יש למשתמש לקרוא רְשׁוּת.

לפיכך, אנו מצפים שרשימת התוצאות מכילה רק את ההודעה הראשונה:

@Test @WithMockUser (שם משתמש = "מנהל") חלל ציבורי שניתן UserManager_whenFindAllMessage_thenReturnFirstMessage () {רשימת פרטים = repo.findAll (); assertNotNull (פרטים); assertEquals (1, details.size ()); assertEquals (FIRST_MESSAGE_ID, details.get (0) .getId ()); }

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

לפיכך, אנו מצפים שרשימת התוצאות תכיל את כל שלוש ההודעות:

@Test @WithMockUser (תפקידים = {"EDITOR"}) חלל ציבורי givenRoleEditor_whenFindAllMessage_thenReturn3Message () {רשימה פרטים = repo.findAll (); assertNotNull (פרטים); assertEquals (3, details.size ()); }

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

@Test @WithMockUser (שם משתמש = "מנהל") חלל ציבורי שניתן UserManager_whenFind1stMessageByIdAndUpdateItsContent_thenOK () {NoticeMessage firstMessage = repo.findById (FIRST_MESSAGE_ID); assertNotNull (firstMessage); assertEquals (FIRST_MESSAGE_ID, firstMessage.getId ()); firstMessage.setContent (EDITTED_CONTENT); repo.save (firstMessage); NoticeMessage editedFirstMessage = repo.findById (FIRST_MESSAGE_ID); assertNotNull (editedFirstMessage); assertEquals (FIRST_MESSAGE_ID, editedFirstMessage.getId ()); assertEquals (EDITTED_CONTENT, editedFirstMessage.getContent ()); }

אבל אם משתמש כלשהו עם ROLE_EDITOR תפקיד מעדכן את תוכן ההודעה הראשונה - המערכת שלנו תשלח הודעה AccessDeniedException:

@Test (צפוי = AccessDeniedException.class) @WithMockUser (תפקידים = {"EDITOR"}) חלל ציבורי givenRoleEditor_whenFind1stMessageByIdAndUpdateContent_thenFail () {NoticeMessage firstMessage = repo.findByIdAGE (FIRST_M) assertNotNull (firstMessage); assertEquals (FIRST_MESSAGE_ID, firstMessage.getId ()); firstMessage.setContent (EDITTED_CONTENT); repo.save (firstMessage); }

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

@Test @WithMockUser (username = "hr") חלל ציבורי givenUsernameHr_whenFindMessageById2_thenOK () {NoticeMessage secondMessage = repo.findById (SECOND_MESSAGE_ID); assertNotNull (secondMessage); assertEquals (SECOND_MESSAGE_ID, secondMessage.getId ()); } @Test (צפוי = AccessDeniedException.class) @WithMockUser (username = "hr") חלל ציבורי givenUsernameHr_whenUpdateMessageWithId2_thenFail () {NoticeMessage secondMessage = NoticeMessage new (); secondMessage.setId (SECOND_MESSAGE_ID); secondMessage.setContent (EDITTED_CONTENT); repo.save (secondMessage); }

5. מסקנה

עברנו תצורה בסיסית ושימוש ב- אביב ACL במאמר זה.

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

כברירת מחדל, אנו מוגבלים להרשאה מוגדרת מראש ב- BasePermissioכיתה n.

לבסוף, ניתן למצוא את יישום המדריך הזה ב- Github.


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