我正在编写一个套接字服务器(没有 web 应用程序!)应用程序,并希望使用基于方法的安全性来处理我的 ACL 需求。我按照我找到的一个小教程 spring security by example
到目前为止我配置:
<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" />
使用服务 bean:
@Named
public class ChannelService {
@PreAuthorize("isAuthenticated() and hasPermission(#channel, 'CHANNEL_WRITE')")
public void writeMessage(Channel channel, String message) { ... }
}
一切都编译,应用程序启动并正常工作,但没有访问控制。我的调试日志显示我的 Evaluator 从未被调用过。
当我尝试使用
@Secured
进行类似操作时注释 注释已评估并且访问被拒绝。但简单的基于角色的安全性不足以满足我的要求。编辑
做了一些更多的测试:当我只配置secure-annotations =“启用”时,基于角色的安全工作。在 ADDITION 中配置 pre-post-annotations="enabled"时,既不保护也不预授权作品。当我只配置前后注释时,它仍然不起作用。
编辑2
更多测试:
只有secure_annotations="enabled"对我的channelservice 的调用通过Cglib2AopProxy
一旦我激活了 pre-post-annotations,调用就会直接进入 channelservice。没有拦截器,没有代理,什么都没有。
我有点绝望了……
编辑3
我在这里调试记录了我的测试运行是 spring-security 的一部分
只有安全注释=“启用”
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.springframework.security.authentication.UsernamePasswordAuthenticationToken@312e8aef: Principal: mystuff.UserId@ced1752b; 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: org.springframework.security.access.vote.RoleVoter@1cfe174, returned: 0
2012-04-12 13:36:56,185 DEBUG [Timer-1] o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.access.vote.AuthenticatedVoter@da89a7, returned: 0
使用 pre-post-annotations="enabled"
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
据我了解,这个日志输出 spring 没有意识到我的 bean 需要被代理,所以它们不是,所以我没有得到安全性。
EDIT4
我调试记录了完整的 sprint 启动...(那是一个大日志),在那里我发现:
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)
有没有办法找出原因?因为据我了解。因为@preauthorize bean 应该是合格的。只有secure-annotations="enabled"我得到一个后处理日志。
最佳答案
这个配置对我来说就像预期的那样工作:
<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" />
我收到了日志消息:
WARN org.springframework.security.access.expression.DenyAllPermissionEvaluator -
Denying user jack permission 'CHANNEL_WRITE' on object Channel[ name=null ]
还有一个异常(exception):
org.springframework.security.access.AccessDeniedException: Access is denied
从一个简单的测试:
@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");
}
}
我做的不同的一件事是在服务和 JDK 代理上使用接口(interface)而不是 cglib:
public interface ChannelService {
void writeMessage(Channel channel, String message);
}
和:
@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);
}
}
更新1:
通过这个简化的配置,我得到了相同的结果:
<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>
更新2:
来自 Edit4 的调试消息表明
channelService
可能根本没有 bean 代理,因为它被归类为 not eligible for auto-proxying
. This qiestion回答类似问题 - 尽量不要使用 @Autowired
或任何其他基于 BeanPostProcessors
的机制设置涉及安全检查的 bean(即 myPermissionEvaluator
)。更新3:
你 不能在负责安全检查的 bean 中使用安全资源(即服务)!这会创建一个依赖循环,并且是您的配置中的错误。你 必须使用情人级别访问(即 DAO)检查权限,任何 不安全 !使用安全资源实现安全检查是 不是你想做的 .
如果尽管使用了不安全的资源,
@Autowired
事情没有按预期工作,请尝试对涉及安全检查的所有 bean 使用老式 XML 配置样式。还要记住 <context:component-scan />
实际上是 BeanDefinitionRegistryPostProcessor
并将扫描的 bean 引入 BeanFactory
毕竟所有在 XML 中声明的都已经存在了。
关于spring - 预授权不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10110374/