2012-04-11 7 views
5

Estoy escribiendo una aplicación de servidor de socket (¡no aplicación web!) Y quiero usar seguridad basada en métodos para manejar mis necesidades de ACL. i seguido de un pequeño tutorial que encontré spring security by examplePreautorizar no funciona

hasta ahora he configurado:

<security:global-method-security pre-post-annotations="enabled"> 
    <security:expression-handler ref="expressionHandler" /> 
</security:global-method-security> 
<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"> 
    <property name="permissionEvaluator"> 
     <bean id="permissionEvaluator" class="myPermissionEvaluator" /> 
    </property> 
</bean> 

<security:authentication-manager id="authenticationmanager"> 
    <security:authentication-provider ref="authenticationprovider" /> 
</security:authentication-manager> 
<bean id="authenticationprovider" class="myAuthenticationProvider" /> 

Con un grano de servicio:

@Named 
public class ChannelService { 
    @PreAuthorize("isAuthenticated() and hasPermission(#channel, 'CHANNEL_WRITE')") 
    public void writeMessage(Channel channel, String message) { ... } 
} 

Todo compila y se inicia la aplicación y funciona bien, pero sin control de acceso . Mi registro de depuración muestra que mi Evaluador nunca se llama.

Cuando intenté algo similar con una anotación @Secured, se evaluó la anotación y se denegó el acceso. pero la seguridad basada en roles simples no es suficiente para mis requisitos.

EDITAR hicieron algunas pruebas más: cuando se configura sólo aseguró anotaciones =-obras de seguridad basadas en el papel "activado". cuando configure pre-post-annotations = "enabled" en ADDITION, ni los trabajos asegurados ni los preautorizados funcionan. cuando configuro solo anotaciones previas a la publicación, todavía no funciona.

Edit2

más pruebas: con sólo secured_annotations = "habilitadas" la llamada a mi ChannelService pasa por el Cglib2AopProxy tan pronto como puedo activar pre-post-anotaciones las tierras llamada directamente en el ChannelService . sin interceptor, sin proxy, nada.

estoy haciendo un poco desesperado ...

Edit3

I-debug registra mis testruns aquí es la parte para la primavera-seguridad

con sólo-anotaciones garantizados =" habilitado"

2012-04-12 13:36:46,171 INFO [main] o.s.s.c.SpringSecurityCoreVersion - You are running with Spring Security Core 3.1.0.RELEASE 
2012-04-12 13:36:46,174 INFO [main] o.s.s.c.SecurityNamespaceHandler - Spring Security 'config' module version is 3.1.0.RELEASE 
2012-04-12 13:36:49,042 DEBUG [main] o.s.s.a.m.DelegatingMethodSecurityMetadataSource - Caching method [CacheKey[mystuff.UserService; public void mystuff.UserService.serverBan(java.lang.String,mystuff.models.User,org.joda.time.DateTime)]] with attributes [user] 
2012-04-12 13:36:49,138 DEBUG [main] o.s.s.a.i.a.MethodSecurityInterceptor - Validated configuration attributes 
2012-04-12 13:36:49,221 DEBUG [main] o.s.s.a.m.DelegatingMethodSecurityMetadataSource - Caching method [CacheKey[mystuff.ChannelService; public void mystuff.ChannelService.writeMessage(mystuff.models.Channel,java.lang.String)]] with attributes [blubb] 
2012-04-12 13:36:51,159 DEBUG [main] o.s.s.a.ProviderManager - Authentication attempt using mystuff.GlobalchatAuthenticationProvider 
2012-04-12 13:36:56,166 DEBUG [Timer-1] o.s.s.a.ProviderManager - Authentication attempt using mystuff.GlobalchatAuthenticationProvider 
2012-04-12 13:36:56,183 DEBUG [Timer-1] o.s.s.a.i.a.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public void mystuff.ChannelService.writeMessage(mystuff.models.Channel,java.lang.String); target is of class [mystuff.ChannelService]; Attributes: [blubb] 
2012-04-12 13:36:56,184 DEBUG [Timer-1] o.s.s.a.i.a.MethodSecurityInterceptor - Previously Authenticated: org.springframew[email protected]312e8aef: Principal: [email protected]; Credentials: [PROTECTED]; Authenticated: true; Details: null; Not granted any authorities 
Exception in thread "Timer-1" org.springframework.security.access.AccessDeniedException: Access is denied 
    at org.springframework.security.access.vote.AbstractAccessDecisionManager.checkAllowIfAllAbstainDecisions(AbstractAccessDecisionManager.java:70) 
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:88) 
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:205) 
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:59) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622) 
    at mystuff.ChannelService$$EnhancerByCGLIB$$3ad5e57f.writeMessage(<generated>) 
    at mystuff.run(DataGenerator.java:109) 
    at java.util.TimerThread.mainLoop(Timer.java:512) 
    at java.util.TimerThread.run(Timer.java:462) 
2012-04-12 13:36:56,185 DEBUG [Timer-1] o.s.s.access.vote.AffirmativeBased - Voter: [email protected], returned: 0 
2012-04-12 13:36:56,185 DEBUG [Timer-1] o.s.s.access.vote.AffirmativeBased - Voter: [email protected]a7, returned: 0 

con pre-post-anotaciones = "activado"

2012-04-12 13:39:54,926 INFO [main] o.s.s.c.SpringSecurityCoreVersion - You are running with Spring Security Core 3.1.0.RELEASE 
2012-04-12 13:39:54,929 INFO [main] o.s.s.c.SecurityNamespaceHandler - Spring Security 'config' module version is 3.1.0.RELEASE 
2012-04-12 13:39:54,989 INFO [main] o.s.s.c.m.GlobalMethodSecurityBeanDefinitionParser - Using bean 'expressionHandler' as method ExpressionHandler implementation 
2012-04-12 13:39:59,812 DEBUG [main] o.s.s.a.ProviderManager - Authentication attempt mystuff.GlobalchatAuthenticationProvider 
2012-04-12 13:39:59,850 DEBUG [main] o.s.s.a.i.a.MethodSecurityInterceptor - Validated configuration attributes 

Por lo que yo entiendo, este resorte de salida de registro no se da cuenta de que mis granos necesitan ser procesados, por lo que no lo son y por lo tanto no obtengo seguridad.

EDIT4

I depuración de registros de la puesta en marcha de velocidad completa ... (eso es un gran registro) y no lo encuentro:

2012-04-12 14:40:41,385 INFO [main] o.s.c.s.ClassPathXmlApplicationContext - Bean 'channelService' of type [class mystuff.ChannelService] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 

hay una manera de averiguar por qué? porque hasta donde yo lo entiendo debido a @preauthorize, el bean debería ser elegible. con solo anotaciones seguras = "habilitado" obtengo un registro de procesamiento posterior.

+0

¿Qué versión de Spring and Spring Security estás utilizando? – Roadrunner

+0

@Roadrunner spring 3.1.0.RELEASE en todo. – Laures

+1

¿Se puede depurar si se llama a "PrePostAnnotationSecurityMetadataSource # getAttributes()" para su método cuando se inicializa Spring? – Luciano

Respuesta

5

Esta configuración funcionaba como se esperaba para mí:

<bean id="securityExpressionHandler" 
    class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler" /> 

<bean id="preInvocationAdvice" 
    class="org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice" 
    p:expressionHandler-ref="securityExpressionHandler" /> 

<util:list id="decisionVoters"> 
    <bean class="org.springframework.security.access.vote.AuthenticatedVoter" /> 
    <bean class="org.springframework.security.access.vote.RoleVoter" /> 
    <bean class="org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter" 
     c:pre-ref="preInvocationAdvice" /> 
</util:list> 

<bean id="accessDecisionManager" 
    class="org.springframework.security.access.vote.UnanimousBased" 
    c:decisionVoters-ref="decisionVoters" /> 

<sec:global-method-security 
    authentication-manager-ref="authenticationManager" 
    access-decision-manager-ref="accessDecisionManager" 
    pre-post-annotations="enabled" /> 

me dieron el mensaje de registro:

WARN org.springframework.security.access.expression.DenyAllPermissionEvaluator - 
    Denying user jack permission 'CHANNEL_WRITE' on object Channel[ name=null ] 

y una excepción:

org.springframework.security.access.AccessDeniedException: Access is denied 

Desde una simple prueba:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration("classpath:META-INF/spring/application-context.xml") 
public class SpringSecurityPrePostTest { 

    @Autowired 
    ChannelService channelService; 

    @Test 
    public void shouldSecureService() throws Exception { 
     Authentication authentication = new UsernamePasswordAuthenticationToken("jack", "sparrow"); 
     SecurityContext securityContext = SecurityContextHolder.getContext(); 
     securityContext.setAuthentication(authentication); 

     channelService.writeMessage(new Channel(), "test"); 
    } 
} 

Una cosa que hice diversa era utilizar la interfaz de un servicio y los proxies de JDK en lugar de cglib:

public interface ChannelService { 

    void writeMessage(Channel channel, String message); 
} 

y:

@Component 
public class ChannelServiceImpl implements ChannelService { 

    private static final Logger LOG = LoggerFactory.getLogger(ChannelServiceImpl.class); 

    @Override 
    @PreAuthorize("isAuthenticated() and hasPermission(#channel, 'CHANNEL_WRITE')") 
    public void writeMessage(Channel channel, String message) { 
     LOG.info("Writing message {} to: {}" , message, channel); 
    } 

} 

Update1:


Con esta configuración simplificada consigo el mismo resultado:

<bean id="securityExpressionHandler" 
    class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler" /> 

<sec:global-method-security 
    authentication-manager-ref="authenticationManager" 
    pre-post-annotations="enabled"> 
    <sec:expression-handler ref="securityExpressionHandler" /> 
</sec:global-method-security> 

Update2:


El mensaje de depuración de Edit4 indica que channelService no puede haber sido proxiado en absoluto, ya que se clasificó como not eligible for auto-proxying. This qiestion responde un problema similar: intente no utilizar @Autowired o cualquier otro mecanismo basado en BeanPostProcessors para configurar los beans que participan en las comprobaciones de seguridad (es decir, myPermissionEvaluator).


Update3:


Usted puede no uso asegurado recursos (es decir, servicios) dentro de los granos de encargados de los controles de seguridad! Esto crea un bucle de dependencia y es un error en su configuración. Usted debe utilizar el acceso de nivel amante (es decir, DAO) para comprobar los permisos, todo lo que es no asegurado! La implementación de comprobaciones de seguridad con recursos seguros es y no lo que desea hacer.

Si a pesar de utilizar recursos no asegurados con @Autowired las cosas no funcionan como se esperaba, intente utilizar el estilo de confiuración XML de la vieja escuela para todos los beans involucrados en las comprobaciones de seguridad.También recuerde que <context:component-scan /> es de hecho un BeanDefinitionRegistryPostProcessor e introduce los beans escaneados en el BeanFactory después de que todos los declarados en XML ya estén allí.

+0

en mi PermissionEvaluator es utilizar el servicio de canal y usuario porque usuario permi Las reacciones en estos objetos se almacenan en estos objetos. Pero '@ inject' de estos beans hace que estos dos servicios se creen antes de PrePostAnnotationSecurityMetadataSource. cuando los elimino, mi evaluador se llama correctamente. ahora solo tengo que descubrir cómo inyectar mis servicios proxy en el evaluador sin causar esto. – Laures

+0

@Laures ver mi respuesta extendida para más consejos. – Roadrunner

+0

los métodos que uso para verificar los permisos no están asegurados (y no lo serán) pero son parte del servicio que tiene métodos seguros. Resolví el problema buscando el objeto de servicio desde el contexto de la aplicación cuando lo necesito por primera vez. quizás no sea la solución más limpia, pero en mi caso no hay un nivel de acceso más bajo. – Laures

-1

funciona, asegúrese de que usted tiene <sec:global-method-security pre-post-annotations="enabled"/> en el servlet de primavera (es decir, donde usted puede tener su <mvc:annotation-driven/>) "sec"

es de xmlns:sec="http://www.springframework.org/schema/security"

+0

como puedes ver ya tengo la etiqueta global-method-security configurada y esta no es una aplicación web, así que no hay servlet ni nada. – Laures

+0

@ Laures, ¿cuál es la anotación con nombre que está utilizando? No es una anotación de primavera, tal vez debería intentar con la anotación de componente –

+0

nombrada es una anotación jsr330 que básicamente significa lo mismo que componente, pero está estandarizada y no específica de primavera. Jsr 330 es implementado por la primavera. – Laures