2012-11-14 14 views
6

Chciałbym zabezpieczyć moją warstwę usług za pomocą Spring Security. Jak wyjaśniono w dokumentacji, potrzebuję użyć MethodSecurityInterceptor, który sprawdzi, czy wywołanie metody jest dozwolone.MethodSecurityInterceptor dla wielu metod

Aby zdecydować, czy wywołanie metody serwisowej jest dozwolone dla danego użytkownika, nie ma to wpływu na wymaganą rolę wywoływanej metody (przy użyciu MethodSecurityMetadataSource), ponieważ zależy to również od parametrów przekazanych do metody. Zgodnie z sugestią w dokumentacji mogę napisać niestandardowy AccessDecisionVoter i uzyskać dostęp do argumentów za pomocą zabezpieczonego obiektu (w tym przypadku MethodInvocation).

Ale moja logika autoryzacji różni się między metodami. Na przykład argumenty mogą się różnić między wieloma metodami, a logika autoryzacji również będzie inna.

widzę dwie opcje:

  • mogę użyć logiki warunkowej w AccessDecisionVoter określić wywoływany metodę i logikę autoryzacji do używania, ale wydaje się być brzydka rozwiązaniem.
  • Potrafię zdefiniować jedną bezpieczną metodę: MethodSecurityInterceptor. Zgodnie z wiosenną dokumentacją, MethodSecurityInterceptor służy do zabezpieczenia wielu metod, dlatego też myślę, że jest inny sposób.

To samo pytanie dotyczy decyzji dostępowej po wywołaniu metody (przy użyciu AfterInvocationProvider).

Jakie są alternatywy?

Odpowiedz

3

można zaimplementować własne metody zabezpieczeń ANNOT oparte na konstrukcji Spring @PreAuthorize("").

Aby pobrać dodatkowe informacje o metodzie (poza wartościami metoda argument) do kontekstu oceny Spel można zaimplementować własną MethodSecurityExpressionHandler

@Service 
public class MySecurityExpressionHandler extends 
    DefaultMethodSecurityExpressionHandler { 

    @Override 
    public StandardEvaluationContext createEvaluationContextInternal(
     Authentication auth, MethodInvocation mi) { 

    StandardEvaluationContext evaluationContext = super 
      .createEvaluationContextInternal(auth, mi); 

    SomeMethodInfoData methodInfoData = mi.getMethod(). ...; 

    evaluationContext.setVariable("someData", <value computed based on method info data>); 
    } 

    return evaluationContext; 
} 

i zarejestrować go w swojej deklaracji global-method-security

<security:global-method-security 
     pre-post-annotations="enabled"> 
     <security:expression-handler 
      ref="mySecurityExpressionHandler" /> 
    </security:global-method-security> 

teraz ty może tworzyć niestandardowe adnotacje zabezpieczeń (i dodatkowe dane opisów procesów w MySecurityExpressionHandler, jeśli są wymagane)

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
@PreAuthorize("#<someData>") 
public @interface CustomSecurityAnnotation { ... } 

na przykład można utworzyć niestandardowe adnotacji, by sprawdzić role użytkowników nie brudząc z ciągów:

@MyUserRoleCheck(MyAppRole.Admin) 
public void someMethod() 
4

osiągnąłem że wdrażając własne AccessDecisionManager że delegaci dostęp decyzji do mojego specjalnego interfejsu AccessDecisionStrategy:

public interface AccessDecisionStrategy { 

    void decide(Authentication authentication, MethodInvocation methodInvocation, ConfigAttribute configAttribute); 

} 

Każdy dostęp strategia decyzja reprezentuje inny sposób podejmowania decyzji dostępu.

można łatwo zaimplementować własną strategię (nawet w innym języku - na przykład Scala):

public class SomeStrategy implements AccessDecisionStrategy { ... 

Jak widać, mój AccessDecisionManager ma mapę strategii. Strategia używana przez menedżera opiera się na argumencie adnotacji.

public class MethodSecurityAccessDecisionManager implements AccessDecisionManager { 

    private Map<String, AccessDecisionStrategy> strategyMap; 

    public MethodSecurityAccessDecisionManager(Map<String, AccessDecisionStrategy> strategyMap) { 
     this.strategyMap = strategyMap; 
    } 

    @Override 
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { 
     ConfigAttribute configAttribute = getSingleConfigAttribute(configAttributes); 
     AccessDecisionStrategy accessDecisionStrategy = strategyMap.get(configAttribute.getAttribute()); 
     if (accessDecisionStrategy == null) { 
      throw new IllegalStateException("AccessDecisionStrategy with name " 
        + configAttribute.getAttribute() + " was not found!"); 
     } 
     try { 
      accessDecisionStrategy.decide(authentication, (MethodInvocation) object, configAttribute); 
     } catch (ClassCastException e) { 
      throw new IllegalStateException(); 
     } 
    } 

    private ConfigAttribute getSingleConfigAttribute(Collection<ConfigAttribute> configAttributes) { 
     if (configAttributes == null || configAttributes.size() != 1) { 
      throw new IllegalStateException("Invalid config attribute configuration"); 
     } 
     return configAttributes.iterator().next(); 
    } 

    @Override 
    public boolean supports(ConfigAttribute attribute) { 
     return true; 
    } 

    @Override 
    public boolean supports(Class<?> clazz) { 
     return clazz.equals(MethodInvocation.class); 
    } 
} 

Teraz, gdy chcę chronić mój sposób mogę umieścić @Secured adnotacji z argumentem, że to nazwa strategii:

@Secured("GetByOwner") 
FlightSpotting getFlightSpotting(Long id); 

można zaimplementować i skonfigurować jak wiele strategii, jak chcesz:

<bean id="methodSecurityAccessDecisionManager" 
     class="some.package.MethodSecurityAccessDecisionManager"> 

    <constructor-arg> 
     <map> 
      <entry key="GetByOwner"> 
       <bean class="some.package.GetByOwnerStrategy"/> 
      </entry> 

      <entry key="SomeOther"> 
       <bean class="some.package.SomeOtherStrategy"/> 
      </entry> 
     </map> 
    </constructor-arg> 

</bean> 

Aby wprowadzić ten menedżer decyzji dotyczącej dostępu, należy wpisać:

<sec:global-method-security secured-annotations="enabled" 
          access-decision-manager-ref="methodSecurityAccessDecisionManager"> 
</sec:global-method-security> 

Ja również realizowany klasa pomocnika do obsługi MethodInvocation argumenty:

import org.aopalliance.intercept.MethodInvocation; 

public class MethodInvocationExtractor<ArgumentType> { 

    private MethodInvocation methodInvocation; 

    public MethodInvocationExtractor(MethodInvocation methodInvocation) { 
     this.methodInvocation = methodInvocation; 
    } 

    public ArgumentType getArg(int num) { 
     try { 
      Object[] arguments = methodInvocation.getArguments(); 
      return (ArgumentType) arguments[num]; 
     } catch (ClassCastException | ArrayIndexOutOfBoundsException e) { 
      throw new IllegalStateException(); 
     } 
    } 

} 

Teraz można łatwo wyodrębnić ciekawe argumenty w kodzie swojej strategii do podjęcia decyzji:

Powiedzmy chcę uzyskać liczbę argumentów 0 że jest typu Long:

MethodInvocationExtractor<Long> extractor = new MethodInvocationExtractor<>(methodInvocation); 
Long id = extractor.getArg(0); 
+1

+1 dla wskaźnika do oddania obiektu do MethodInvocation w zadecydować(). W dokumentacji Spring brakuje tej ważnej informacji ... –

+0

Jeśli ktoś jej potrzebuje, dodałem, jak to zaimplementować za pomocą Java Config (mój projekt używa Spring-Boot) [w tym nowym pytaniu] (http: // stackoverflow. com/q/24983046/2796922). Zobacz moją odpowiedź. – elysch

+0

W wielu przypadkach nie ma potrzeby wdrażania własnego menedżera decyzyjnego. Zamiast tego możesz po prostu napisać metodę i wywołać ją bezpośrednio z '@ Preauthorize', zobacz tę odpowiedź: http://stackoverflow.com/a/39972346/1169324 –

Powiązane problemy