Chciałbym utworzyć klasę, która dodaje niestandardowe metody do użycia w języku wyrażeń bezpieczeństwa Spring w celu autoryzacji opartej na metodach za pośrednictwem adnotacji.
Na przykład chciałbym utworzyć niestandardową metodę, taką jak `` customMethodReturningBoolean '', która będzie używana w następujący sposób:
@PreAuthorize("customMethodReturningBoolean()")
public void myMethodToSecure() {
// whatever
}
Moje pytanie brzmi: Jeśli to możliwe, jaką klasę powinienem utworzyć podklasę, aby utworzyć własne metody, jak zabrać się za konfigurowanie jej w plikach konfiguracyjnych XML wiosny i czy ktoś poda mi przykład niestandardowej metody używanej w ten sposób?
java
spring
spring-security
Paul D. Eden
źródło
źródło
Odpowiedzi:
Musisz podklasować dwie klasy.
Najpierw ustaw nową procedurę obsługi wyrażenia metody
<global-method-security> <expression-handler ref="myMethodSecurityExpressionHandler"/> </global-method-security>
myMethodSecurityExpressionHandler
będzie podklasą,DefaultMethodSecurityExpressionHandler
której nadpisujecreateEvaluationContext()
, ustawiając podklasęMethodSecurityExpressionRoot
naMethodSecurityEvaluationContext
.Na przykład:
@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; }
źródło
MethodSecurityExpressionRoot
będąc pakiet-prywatnego ?Żadna z wymienionych technik już nie zadziała. Wygląda na to, że Spring przeszedł wiele starań, aby uniemożliwić użytkownikom zastąpienie SecurityExpressionRoot.
EDIT 11/19/14 Setup Spring, aby używać adnotacji bezpieczeństwa:
<beans ... xmlns:sec="http://www.springframework.org/schema/security" ... > ... <sec:global-method-security pre-post-annotations="enabled" />
Utwórz fasolę w ten sposób:
@Component("mySecurityService") public class MySecurityService { public boolean hasPermission(String key) { return true; } }
Następnie zrób coś takiego w swoim jsp:
<sec:authorize access="@mySecurityService.hasPermission('special')"> <input type="button" value="Special Button" /> </sec:authorize>
Lub opisz metodę:
@PreAuthorize("@mySecurityService.hasPermission('special')") public void doSpecialStuff() { ... }
Dodatkowo możesz użyć języka Spring Expression Language w swoich
@PreAuthorize
adnotacjach, aby uzyskać dostęp do bieżącego uwierzytelnienia, a także argumentów metody.Na przykład:
@Component("mySecurityService") public class MySecurityService { public boolean hasPermission(Authentication authentication, String foo) { ... } }
Następnie zaktualizuj swój,
@PreAuthorize
aby pasował do nowej sygnatury metody:@PreAuthorize("@mySecurityService.hasPermission(authentication, #foo)") public void doSpecialStuff(String foo) { ... }
źródło
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
do uzyskania aktualnego tokena uwierzytelniania.Dzięki ericacm , ale to nie działa z kilku powodów:
Różnice polegają na tym, że wywołujemy istniejącą metodę createEvaluationContext , a następnie dodajemy nasz niestandardowy obiekt główny. Na koniec właśnie zwróciłem typ obiektu StandardEvaluationContext, ponieważ MethodSecurityEvaluationContext nie zostałoby rozwiązane w kompilatorze (oba pochodzą z tego samego interfejsu). To jest kod, który mam teraz w produkcji.
Spraw, aby MethodSecurityExpressionHandler używał naszego niestandardowego katalogu głównego:
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; } }
Spowoduje to zastąpienie domyślnego katalogu głównego przez rozszerzenie SecurityExpressionRoot . Tutaj zmieniłem nazwę hasRole na 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); } }
Na koniec zaktualizuj securityContext.xml (i upewnij się, że odwołuje się do niego w pliku 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" />
Uwaga: adnotacja @Secured nie zaakceptuje tego zastąpienia, ponieważ przechodzi przez inną procedurę obsługi walidacji. Tak więc w powyższym xml wyłączyłem je, aby zapobiec późniejszym nieporozumieniom.
źródło