这是关于Spring
v.4(MVC +安全性)。我已经实现了UserDetailsServiceImpl
,其中loadUserByUsername
方法内部用户被授予其权限。简单地说:
public UserDetails loadUserByUsername(String username) {
...
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ADMIN"));
return new org.springframework.security.core.userdetails.User(username, password, enabled, true, true, true, authorities);
...
}
我有安全控制器,其中我用@Secured
注释注释了方法:
@Secured("ADMIN")
@RequestMapping(value = "/users", method = RequestMethod.GET)
public String users(Model model ...) { ... }
正如您在loadUserByUsername
内看到的那样,我明确向用户授予ADMIN
角色。
但是当我尝试访问/users
时,我得到Access is denied
例外:
2016-04-19 10:25:16,899 DEBUG(http-nio-8080-exec-9) [org.springframework.security.web.access.ExceptionTranslationFilter] - 访问被拒绝(用户不是匿名的);委托给 AccessDeniedHandler org.springframework.security.access.AccessDeniedException:访问是 否认了 org.springframework.security.access.vote.AbstractAccessDecisionManager.checkAllowIfAllAbstainDecisions(AbstractAccessDecisionManager.java:70) 在 org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:88) 在 org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232) 在 org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 在 org.springframework.aop.framework.CglibAopProxy $ DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655) ...
(没有@Secured
注释一切正常)。
那么,我在这里错过了什么?
答案 0 :(得分:9)
令人惊讶的是,问题在于角色名称。由于defaultRolePrefix
设置为ROLE_
(请参阅org.springframework.security.access.vote.RoleVoter
类),所有角色的名称都应以ROLE_
前缀开头。换句话说,当我改变了
authorities.add(new SimpleGrantedAuthority("ADMIN"));
到
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
和@Secured("ADMIN")
到@Secured("ROLE_ADMIN")
- 一切都很好。
答案 1 :(得分:3)
我也遇到过这个问题。 Spring Security 4会自动为ROLE_
的任何角色添加前缀。有关详细信息,请参阅Spring Security 3.x to 4.x migration guide。
ROLE_
前缀根据迁移指南中的建议,以下BeanPostProcessor
可用于禁用自动ROLE_
前缀:
public class DefaultRolesPrefixPostProcessor implements BeanPostProcessor,
PriorityOrdered {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
// Remove this if you are not using JSR-250
if (bean instanceof Jsr250MethodSecurityMetadataSource) {
((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(null);
}
if (bean instanceof DefaultMethodSecurityExpressionHandler) {
((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
}
if (bean instanceof DefaultWebSecurityExpressionHandler) {
((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
}
if (bean instanceof SecurityContextHolderAwareRequestFilter) {
((SecurityContextHolderAwareRequestFilter)bean).setRolePrefix("");
}
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@Override
public int getOrder() {
return PriorityOrdered.HIGHEST_PRECEDENCE;
}
}
然后将其定义为bean:
@Bean
public static DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() {
return new DefaultRolesPrefixPostProcessor();
}
或者,您可以使用@PreAuthorize
注释检查权限:
@PreAuthorize("hasAuthority('ADMIN')")
确保已启用prePostEnabled
:
@EnableGlobalMethodSecurity(prePostEnabled = true)