Как программно добавить Security в Spring Bean - PullRequest
0 голосов
/ 24 декабря 2009

Я использую spring-security-tiger-2.0.5.

Есть ли способ программно добавить прокси-сервер безопасности в Spring Bean?

Я создаю bean-компонент с помощью BeanDefinitionBuilder и хочу добавить то же поведение, что и в аннотации @Secured.

В качестве параметра будет передан эквивалент @Secured roleName.

Ответы [ 3 ]

1 голос
/ 25 декабря 2009

Чтобы программно внедрить функциональность Spring Security в существующие bean-компоненты, вам может понадобиться использовать контекст приложения Spring Security и зарегистрировать ваши bean-компоненты там:

@Test
public void testSpringSecurity() throws Exception {
    InMemoryXmlApplicationContext ctx = initSpringAndSpringSecurity();

    // Creates new instance
    IMyService secured = (IMyService) ctx.getAutowireCapableBeanFactory()
            .initializeBean(new MyService(), "myService");

    assertTrue(AopUtils.isAopProxy(secured));

    fakeSecurityContext("ROLE_USER");
    secured.getCustomers(); // Works: @Secured("ROLE_USER")

    fakeSecurityContext("ROLE_FOO");
    try {
        secured.getCustomers(); // Throws AccessDenied Exception
        fail("AccessDeniedException expected");
    } catch (AccessDeniedException expected) {
    }
}

private InMemoryXmlApplicationContext initSpringAndSpringSecurity() {
    InMemoryXmlApplicationContext ctx = new InMemoryXmlApplicationContext(
            "<b:bean id='authenticationManager' class='org.springframework.security.MockAuthenticationManager' /> "
                    + "<b:bean id='accessDecisionManager' class='org.springframework.security.vote.UnanimousBased'><b:property name='decisionVoters'><b:list><b:bean class='org.springframework.security.vote.RoleVoter'/></b:list></b:property></b:bean>"
                    + "<b:bean id='objectDefinitionSource' class='org.springframework.security.annotation.SecuredMethodDefinitionSource' /> "
                    + "<b:bean id='autoproxy' class='org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator'/>"
                    + "<b:bean id='methodSecurityAdvisor' class='org.springframework.security.intercept.method.aopalliance.MethodDefinitionSourceAdvisor' autowire='constructor'/>"
                    + "<b:bean id='securityInterceptor' class='org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor'><b:property name='authenticationManager' ref='authenticationManager' /><b:property name='accessDecisionManager' ref='accessDecisionManager' /><b:property name='objectDefinitionSource' ref='objectDefinitionSource' /></b:bean>");
    return ctx;
}

Я использовал контекст приложения в памяти, поскольку MethodDefinitionSourceAdvisor в своей документации заявляет, что автоматическое проксирование включено только для ApplicationContext s. Таким образом, я считаю, что вам нужен контекст приложения для автоматического проксирования, если вы не используете отдельный ProxyFactoryBean для каждого объекта службы. Но так как вы используете аннотацию @Secured, я подозреваю, что это то же самое, что и авто-проксирование.

fakeSecurityContext() просто установил объект Authentication с данной ролью в SecurityContextHolder для целей тестирования.

Вы можете сделать это с функциональностью Spring Core самостоятельно. Предположим, у вас есть служба, которая возвращает список клиентов, и текущий пользователь может просматривать только клиентов с определенным пределом дохода. Следующий тест будет нашим стартом:

@Test
public void testSecurity() throws Exception {
    ClassPathXmlApplicationContext appCtx = new ClassPathXmlApplicationContext(
            "spring.xml");
    IMyService service = (IMyService) appCtx.getBean("secured",
            IMyService.class);
    assertEquals(1, service.getCustomers().size());
}

Это оригинальная реализация сервиса:

public class MyService implements IMyService {

    public List<Customer> getCustomers() {
        return Arrays.asList(new Customer(100000), new Customer(5000));
    }

}

Сконфигурируйте ваш сервисный объект в spring.xml и добавьте метод-перехватчик:

<bean id="service" class="de.mhaller.spring.MyService"></bean>

<bean id="securityInterceptor" class="de.mhaller.spring.MyServiceInterceptor">
    <property name="revenueLimit" value="50000"/>
</bean>

<bean id="secured" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetName" value="service" />
    <property name="interceptorNames">
        <list>
            <value>securityInterceptor</value>
        </list>
    </property>
</bean>

Реализация перехватчика безопасности:

public class MyServiceInterceptor implements MethodInterceptor {

    private int revenueLimit = 10000;
    public void setRevenueLimit(int revenueLimit) {
        this.revenueLimit = revenueLimit;
    }

    @SuppressWarnings("unchecked")
    public Object invoke(MethodInvocation mi) throws Throwable {
        List<Customer> filtered = new ArrayList<Customer>();
        List<Customer> result = (List<Customer>) mi.proceed();
        for (Customer customer : result) {
            if (customer.isRevenueBelow(revenueLimit)) {
                filtered.add(customer);
            }
        }
        return filtered;
    }

}

Преимущество использования такого подхода состоит в том, что вы можете не только декларативно проверять роли текущего пользователя, но и динамически применять корпоративные политики, например, ограничить возвращаемые объекты на основе бизнес-ценностей.

1 голос
/ 28 декабря 2009

спасибо за ответы, но в конечном итоге я получил, что это работает совершенно программно. Это было не легкое решение, но я хотел бы поделиться им.

Прежде всего, я мог бы разбить проблему на 2 части:

1) Создайте прокси для моих собственных бобов.

2) Добавьте роли безопасности для каждого компонента. К сожалению, я не мог добавить основанный на xml pointcut, так как все компоненты были одного и того же класса (универсальный сервис). Я хотел, чтобы каждый бин был защищен по-разному, несмотря на то, что они были одного класса. Я решил использовать ту же декларацию, что и aopalliance ((выполнение . и т. Д.)

Для кого это может заинтересовать, вот как я это сделал:

1) Использовал класс BeanNameAutoProxyCreator у Spring для создания прокси вокруг классов, которые я добавил вручную:

private void addProxies(ConfigurableListableBeanFactory beanFactory, List<String> exportedServices) {
  List<String> interceptorNames = findInterceptorNames(beanFactory);
  BeanNameAutoProxyCreator beanPostProcessor = new BeanNameAutoProxyCreator();
  beanPostProcessor.setBeanNames(exportedServices.toArray(new String[exportedServices.size()]));
  beanPostProcessor.setInterceptorNames(interceptorNames.toArray(new String[interceptorNames.size()]));
  beanPostProcessor.setBeanFactory(beanFactory);
  beanPostProcessor.setOrder(Ordered.LOWEST_PRECEDENCE);
  beanFactory.addBeanPostProcessor(beanPostProcessor);
 }

 @SuppressWarnings("unchecked")
 private List<String> findInterceptorNames(ConfigurableListableBeanFactory beanFactory) {
  List<String> interceptorNames = new ArrayList<String>();
  List<? extends Advisor> list = new BeanFactoryAdvisorRetrievalHelper(beanFactory).findAdvisorBeans();
  for (Advisor ad : list) {
   Advice advice = ad.getAdvice();
   if (advice instanceof MethodInterceptor) {
    Map<String, ?> beansOfType = beanFactory.getBeansOfType(advice.getClass());
    for (String name : beansOfType.keySet()) {
     interceptorNames.add(name);
    }
   }
  }
  return interceptorNames;
 }
  • где exportedServices обозначает бины, которые я хотел прокси
  • Экземпляр beanFactory может быть получен с использованием моей собственной реализации BeanFactoryPostProcessor

2) Для параметрируемой защищенной детали:

Создана реализация метода SpringDefinitionSource. Разработчики этого интерфейса используются MethodSecurityProxy для извлечения securedObjects во время выполнения.

   @SuppressWarnings("unchecked")
    @Override
 public ConfigAttributeDefinition getAttributes(Object object) throws                                              IllegalArgumentException {
 if (!(object instanceof ReflectiveMethodInvocation)) {
  return null;
 }
 ReflectiveMethodInvocation invokation = (ReflectiveMethodInvocation) object;
 if (!(invokation.getThis() instanceof MyService)) {
  return null;
 }
 MyService service = (MyService) invokation.getThis();
  Map<String, String> map =getProtectedServiceMethodMap(service);
  String roles = map.get(MethodKeyGenerator.generate(invokation.getMethod()));
  return roles == null ? null : new ConfigAttributeDefinition(StringUtils.delimitedListToStringArray(roles, ","));
 }
  • Обратите внимание, что я должен был поместить в кеш методы, которые я хотел защитить для каждого экземпляра объекта.

Самой сложной задачей было зарегистрировать мой метод MethodDefinitionSource в applicationContext, чтобы MethodSecurityInterceptor узнал об этом:

   private void addCrudDefinitionSource() {
 DelegatingMethodDefinitionSource source = (DelegatingMethodDefinitionSource)    beanFactory.getBean(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE);
     try {
     Field field = source.getClass().getDeclaredField("methodDefinitionSources");
     field.setAccessible(true);
     List list = (List) field.get(source);
     list.add(new MyMethodDefinitionSource());
     } catch (Exception e) {
     e.printStackTrace();
     }
            }

Наконец, для проверки точек входа метода в стиле aop я использовал код, подобный следующему:

  private void addToCache(){ // Ommiting parameters
     PointcutExpression expression = parser.parsePointcutExpression(fullExpression);
     Method[] methods = clazzToCheck.getMethods();
     for (int i = 0; i < methods.length; i++) {
     Method currentMethod = methods[i];
     boolean matches = expression.matchesMethodExecution(currentMethod).alwaysMatches();
     if (matches){
 //Add to Cache
     }
     }
0 голосов
/ 25 декабря 2009

вы должны проверить org.springframework.aop.framework.ProxyFactory учебный класс. Оберните ваш компонент с помощью ProxyFactory и добавьте перехватчик безопасности

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...