วิธีสร้างเมธอดแบบกำหนดเองสำหรับใช้ในคำอธิบายประกอบภาษานิพจน์การรักษาความปลอดภัย spring


92

ฉันต้องการสร้างคลาสที่เพิ่มวิธีการที่กำหนดเองสำหรับใช้ในภาษานิพจน์การรักษาความปลอดภัยในฤดูใบไม้ผลิสำหรับการอนุญาตตามวิธีการผ่านคำอธิบายประกอบ

ตัวอย่างเช่นฉันต้องการสร้างวิธีการที่กำหนดเองเช่น 'customMethodReturningBoolean' เพื่อใช้ในลักษณะนี้:

  @PreAuthorize("customMethodReturningBoolean()")
  public void myMethodToSecure() { 
    // whatever
  }

คำถามของฉันคือสิ่งนี้ ถ้าเป็นไปได้คลาสใดที่ฉันควรจะสร้างเมธอดแบบกำหนดเองของฉันฉันจะไปกำหนดค่าในไฟล์คอนฟิกูเรชัน spring xml ได้อย่างไรและมีใครบางคนให้ตัวอย่างวิธีการกำหนดเองที่ใช้ในลักษณะนี้


2
ฉันไม่มีเวลาพิมพ์คำตอบในตอนนี้ แต่ฉันทำตามคู่มือนี้แล้วและมันก็ใช้ได้ดีมาก: baeldung.com/… ฉันใช้ Spring Security 5.1.1
พอล

คำตอบ:


35

คุณจะต้องซับคลาสสองคลาส

ขั้นแรกตั้งค่าตัวจัดการนิพจน์วิธีการใหม่

<global-method-security>
  <expression-handler ref="myMethodSecurityExpressionHandler"/>
</global-method-security>

myMethodSecurityExpressionHandlerจะเป็นคลาสย่อยDefaultMethodSecurityExpressionHandlerที่ลบล้างcreateEvaluationContext()การตั้งค่าคลาสย่อยของMethodSecurityExpressionRootบนMethodSecurityEvaluationContext.

ตัวอย่างเช่น:

@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
    MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
    MethodSecurityExpressionRoot root = new MyMethodSecurityExpressionRoot(auth);
    root.setTrustResolver(trustResolver);
    root.setPermissionEvaluator(permissionEvaluator);
    root.setRoleHierarchy(roleHierarchy);
    ctx.setRootObject(root);

    return ctx;
}

อืมฟังดูเหมือนเป็นความคิดที่ดี แต่คุณสมบัติทั้งหมดของDefaultMethodSecurityExpressionHandlerเป็นแบบส่วนตัวโดยไม่มี accessors ดังนั้นฉันจึงอยากรู้ว่าคุณขยายชั้นเรียนได้อย่างไรโดยไม่มีภาพสะท้อนที่น่าเกลียด ขอบคุณ.
Joseph Lust

1
คุณหมายถึง trustResolver ฯลฯ ? ผู้ที่ทุกคนมี setters ใน DefaultMethodSecurityExpressionHandler (อย่างน้อยในฤดูใบไม้ผลิการรักษาความปลอดภัย 3.0) เห็น: static.springsource.org/spring-security/site/apidocs/org/...
sourcedelica

3
@ericacm คุณจะได้รับMethodSecurityExpressionRootความเป็นส่วนตัวได้อย่างไร?
ค. รอส

176

เทคนิคที่กล่าวถึงจะไม่ได้ผลอีกต่อไป ดูเหมือนว่า Spring จะผ่านช่วงเวลาอันยาวนานในการป้องกันไม่ให้ผู้ใช้แทนที่ SecurityExpressionRoot

แก้ไข 11/19/14 ตั้งค่า Spring เพื่อใช้คำอธิบายประกอบความปลอดภัย:

<beans ... xmlns:sec="http://www.springframework.org/schema/security" ... >
...
<sec:global-method-security pre-post-annotations="enabled" />

สร้างถั่วดังนี้:

@Component("mySecurityService")
public class MySecurityService {
    public boolean hasPermission(String key) {
        return true;
    }
}

จากนั้นทำสิ่งนี้ใน jsp ของคุณ:

<sec:authorize access="@mySecurityService.hasPermission('special')">
    <input type="button" value="Special Button" />
</sec:authorize>

หรือใส่คำอธิบายประกอบวิธี:

@PreAuthorize("@mySecurityService.hasPermission('special')")
public void doSpecialStuff() { ... }

นอกจากนี้คุณสามารถใช้Spring Expression Languageใน@PreAuthorizeคำอธิบายประกอบของคุณเพื่อเข้าถึงการพิสูจน์ตัวตนปัจจุบันรวมทั้งอาร์กิวเมนต์ของวิธีการ

ตัวอย่างเช่น:

@Component("mySecurityService")
public class MySecurityService {
    public boolean hasPermission(Authentication authentication, String foo) { ... }
}

จากนั้นอัปเดตของคุณ@PreAuthorizeให้ตรงกับลายเซ็นวิธีใหม่:

@PreAuthorize("@mySecurityService.hasPermission(authentication, #foo)")
public void doSpecialStuff(String foo) { ... }

6
@Bosh ในเมธอด hasPermission ของคุณคุณสามารถใช้Authentication auth = SecurityContextHolder.getContext().getAuthentication();เพื่อรับโทเค็นการพิสูจน์ตัวตนปัจจุบัน
James Watkins

2
ขอบคุณเจมส์สำหรับคำตอบของคุณ ฉันต้องกำหนด mySecurityService ในไฟล์ Spring config หรือไม่
WowBow

2
คุณไม่จำเป็นต้องกำหนด mySecurityService ในไฟล์ XML ใด ๆ หากคุณมีการตั้งค่าการสแกนคอมโพเนนต์สำหรับแพ็กเกจที่มีบริการอยู่หากคุณไม่มีการสแกนคอมโพเนนต์ที่ตรงกันคุณต้องใช้ข้อกำหนด xml bean @PreAuthorize มาจาก org.springframework.security
James Watkins

3
คุณอาจต้องระบุชื่อของ bean ในคำอธิบายประกอบดังนี้ @Component ("mySecurityService") หรือใช้คำอธิบายประกอบ @Named
James Watkins

1
@VJS โปรดดูการแก้ไขที่ฉันทำ คุณจะต้องกำหนดค่า spring เพื่อใช้คำอธิบายประกอบเหล่านี้ ฉันแปลกใจที่ไม่มีใครบ่นเกี่ยวกับรายละเอียดที่ขาดหายไปนี้ :)
James Watkins

14

ขอบคุณericacmแต่ไม่ได้ผลด้วยเหตุผลบางประการ:

  • คุณสมบัติของDefaultMethodSecurityExpressionHandlerเป็นแบบส่วนตัว (การมองเห็นการสะท้อนจะไม่เป็นที่ต้องการ)
  • อย่างน้อยใน Eclipse ของฉันฉันไม่สามารถแก้ไขอ็อบเจ็กต์MethodSecurityEvaluationContext

ความแตกต่างคือเราเรียกเมธอดcreateEvaluationContextที่มีอยู่แล้วเพิ่มวัตถุรูทที่กำหนดเองของเรา ในที่สุดฉันก็ส่งคืนชนิดอ็อบเจ็กต์StandardEvaluationContextเนื่องจาก MethodSecurityEvaluationContext ไม่สามารถแก้ไขได้ในคอมไพเลอร์ (ทั้งสองอย่างมาจากอินเทอร์เฟซเดียวกัน) นี่คือรหัสที่ฉันมีในการผลิตตอนนี้

ทำให้MethodSecurityExpressionHandlerใช้รูทที่กำหนดเองของเรา:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler  {

    // parent constructor
    public CustomMethodSecurityExpressionHandler() {
        super();
    }

    /**
     * Custom override to use {@link CustomSecurityExpressionRoot}
     * 
     * Uses a {@link MethodSecurityEvaluationContext} as the <tt>EvaluationContext</tt> implementation and
     * configures it with a {@link MethodSecurityExpressionRoot} instance as the expression root object.
     */
    @Override
    public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
        // due to private methods, call original method, then override it's root with ours
        StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi);
        ctx.setRootObject( new CustomSecurityExpressionRoot(auth) );
        return ctx;
    }
}

นี้แทนที่รากเริ่มต้นโดยการขยายSecurityExpressionRoot ที่นี่ฉันเปลี่ยนชื่อ hasRole เป็น hasEntitlement:

public class CustomSecurityExpressionRoot extends SecurityExpressionRoot  {

    // parent constructor
    public CustomSecurityExpressionRoot(Authentication a) {
        super(a);
    }

    /**
     * Pass through to hasRole preserving Entitlement method naming convention
     * @param expression
     * @return boolean
     */
    public boolean hasEntitlement(String expression) {
        return hasRole(expression);
    }

}

สุดท้ายอัปเดต securityContext.xml (และตรวจสอบให้แน่ใจว่าอ้างอิงจาก applcationContext.xml ของคุณ):

<!-- setup method level security using annotations -->
<security:global-method-security
        jsr250-annotations="disabled"
        secured-annotations="disabled"
        pre-post-annotations="enabled">
    <security:expression-handler ref="expressionHandler"/>
</security:global-method-security>

<!--<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">-->
<bean id="expressionHandler" class="com.yourSite.security.CustomMethodSecurityExpressionHandler" />

หมายเหตุ:หมายเหตุประกอบ @Secured จะไม่ยอมรับการแทนที่นี้เนื่องจากรันผ่านตัวจัดการการตรวจสอบความถูกต้องอื่น ดังนั้นใน xml ด้านบนฉันจึงปิดใช้งานเพื่อป้องกันความสับสนในภายหลัง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.