Як створити власні методи для використання у весняних анотаціях мови виразів безпеки


92

Я хотів би створити клас, який додає власні методи для використання у весняній мові виразів безпеки для авторизації на основі методів за допомогою анотацій.

Наприклад, я хотів би створити власний метод, такий як 'customMethodReturningBoolean', який буде використовуватися якось так:

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

Моє питання таке. Якщо це можливо, який клас слід підкласувати для створення власних методів, як би я налаштував його у весняних файлах конфігурації xml і прийшов, щоб хтось дав мені приклад користувацького методу, використовуваного таким чином?


1
Зараз я не встигаю набрати відповідь, але я дотримувався цього посібника, і він чудово працював: 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 є приватними без аксесуарів, тому мені було цікаво, як ви розширили клас без жодних потворних роздумів. Дякую.
Джозеф Луст

1
Ви маєте на увазі trustResolver тощо? Ті , у всіх сеттерів в DefaultMethodSecurityExpressionHandler (принаймні , в Spring Security 3.0) Див: static.springsource.org/spring-security/site/apidocs/org / ...
sourcedelica

3
@ericacm Як Ви обходьте MethodSecurityExpressionRootбути приватним пакет ?
К. Росс

175

Жодна із згаданих методик вже не буде працювати. Здається, що Spring пройшов багато часу, щоб не дати користувачам замінити SecurityExpressionRoot.

EDIT 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();для отримання поточного маркера автентифікації.
Джеймс Уоткінс,

2
Дякую Джеймсу за вашу відповідь. Чи потрібно визначати mySecurityService у весняному конфігураційному файлі?
WowBow

2
Вам не потрібно визначати mySecurityService в будь-якому XML-файлі, якщо у вас налаштовано сканування компонентів для пакета, в якому знаходиться служба. Якщо у вас немає відповідного сканування компонентів, то ви повинні використовувати визначення компонента xml. @PreAuthorize походить від org.springframework.security
James Watkins

3
Можливо, вам доведеться вказати ім'я компонента для анотації таким чином: @Component ("mySecurityService") або використовувати анотацію @Named.
James Watkins

1
@VJS Будь ласка, подивіться редагування, яке я зробив. Вам потрібно буде налаштувати spring, щоб використовувати ці анотації. Я здивований, що ніхто інший не скаржився на цю важливу відсутність деталей :)
Джеймс Уоткінс

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

Це замінює root за замовчуванням, розширюючи 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 я відключив їх, щоб уникнути подальшої плутанини.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.