Cách tạo các phương thức tùy chỉnh để sử dụng trong chú thích ngôn ngữ biểu thức bảo mật mùa xuân


92

Tôi muốn tạo một lớp bổ sung các phương thức tùy chỉnh để sử dụng trong ngôn ngữ biểu thức bảo mật mùa xuân để ủy quyền dựa trên phương thức thông qua chú thích.

Ví dụ: tôi muốn tạo một phương thức tùy chỉnh như 'customMethodReturningBoolean' để được sử dụng bằng cách nào đó như sau:

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

Câu hỏi của tôi là này. Nếu có thể, tôi nên phân lớp nào để tạo các phương thức tùy chỉnh của mình, tôi sẽ định cấu hình nó như thế nào trong tệp cấu hình xml mùa xuân và đến ai đó cho tôi một ví dụ về phương thức tùy chỉnh được sử dụng theo cách này?


1
Tôi không có thời gian để nhập câu trả lời ngay bây giờ nhưng tôi đã làm theo hướng dẫn này và nó hoạt động tuyệt vời: baeldung.com/… Tôi đang sử dụng Spring Security 5.1.1.
Paul

Câu trả lời:


35

Bạn sẽ cần phân lớp hai lớp.

Đầu tiên, hãy đặt một trình xử lý biểu thức phương thức mới

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

myMethodSecurityExpressionHandlersẽ là một lớp con trong DefaultMethodSecurityExpressionHandlerđó ghi đè createEvaluationContext(), thiết lập một lớp con của MethodSecurityExpressionRoottrên MethodSecurityEvaluationContext.

Ví dụ:

@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;
}

Hmm, nghe có vẻ là một ý tưởng hay, nhưng tất cả các thuộc tính của DefaultMethodSecurityExpressionHandler là riêng tư mà không có trình truy cập, vì vậy tôi tò mò làm thế nào bạn mở rộng lớp mà không có bất kỳ phản chiếu xấu xí nào. Cảm ơn.
Joseph Lust

1
Ý bạn là trustResolver, v.v.? Những tất cả đều có setters trong DefaultMethodSecurityExpressionHandler (ít nhất là trong mùa xuân an ninh 3.0) Xem: static.springsource.org/spring-security/site/apidocs/org/...
sourcedelica

3
@ericacm Bạn làm cách nào để MethodSecurityExpressionRoottrở thành gói riêng tư ?
C. Ross

175

Không có kỹ thuật nào được đề cập sẽ hoạt động nữa. Có vẻ như Spring đã trải qua một thời gian dài để ngăn người dùng ghi đè SecurityExpressionRoot.

CHỈNH SỬA 19/11/14 Thiết lập mùa xuân để sử dụng chú thích bảo mật:

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

Tạo một hạt đậu như thế này:

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

Sau đó, làm một cái gì đó như thế này trong jsp của bạn:

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

Hoặc chú thích một phương pháp:

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

Ngoài ra, bạn có thể sử dụng Ngôn ngữ biểu thức mùa xuân trong các @PreAuthorizechú thích của mình để truy cập xác thực hiện tại cũng như các đối số của phương thức.

Ví dụ:

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

Sau đó, cập nhật của bạn @PreAuthorizeđể khớp với chữ ký phương pháp mới:

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

6
@Bosh trong phương thức hasPermission của bạn, bạn có thể sử dụng Authentication auth = SecurityContextHolder.getContext().getAuthentication();để lấy mã xác thực hiện tại.
James Watkins

2
Cảm ơn James cho câu trả lời của bạn. Tôi có phải xác định mySecurityService trong tệp cấu hình mùa xuân không?
WowBow

2
Bạn không cần phải xác định mySecurityService trong bất kỳ tệp XML nào nếu bạn có thiết lập quét thành phần cho gói chứa dịch vụ. Nếu bạn không có quét thành phần phù hợp, thì bạn phải sử dụng định nghĩa bean xml. @PreAuthorize đến từ org.springframework.security
James Watkins

3
Bạn có thể cần chỉ định tên của bean cho chú thích như sau: @Component ("mySecurityService") hoặc sử dụng chú thích @Named.
James Watkins

1
@VJS Vui lòng xem bản chỉnh sửa mà tôi đã thực hiện. Bạn sẽ cần phải định cấu hình mùa xuân để sử dụng các chú thích này. Tôi ngạc nhiên là không ai khác phàn nàn về chi tiết bị thiếu quan trọng này :)
James Watkins

14

Cảm ơn ericacm , nhưng nó không hoạt động vì một số lý do:

  • Các thuộc tính của DefaultMethodSecurityExpressionHandler là riêng tư (khả năng hiển thị phản chiếu không mong muốn)
  • Ít nhất trong Eclipse của tôi, tôi không thể giải quyết một đối tượng MethodSecurityEvaluationContext

Sự khác biệt là chúng tôi gọi phương thức createEvaluationContext hiện có và sau đó thêm đối tượng gốc tùy chỉnh của chúng tôi. Cuối cùng, tôi vừa trả về một kiểu đối tượng StandardEvaluationContext vì MethodSecurityEvaluationContext sẽ không phân giải trong trình biên dịch (cả hai đều từ cùng một giao diện). Đây là mã mà tôi hiện có trong sản xuất.

Đặt MethodSecurityExpressionHandler sử dụng root tùy chỉnh của chúng tôi:

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;
    }
}

Điều này thay thế gốc mặc định bằng cách mở rộng SecurityExpressionRoot . Ở đây tôi đã đổi tên hasRole thành 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);
    }

}

Cuối cùng cập nhật securityContext.xml (và đảm bảo rằng nó được tham chiếu từ applcationContext.xml của bạn):

<!-- 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" />

Lưu ý: chú thích @Secured sẽ không chấp nhận ghi đè này vì nó chạy qua một trình xử lý xác thực khác. Vì vậy, trong xml ở trên, tôi đã vô hiệu hóa chúng để tránh nhầm lẫn sau này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.