我已经遵循了很多线程来为我的rest API实现Spring Security。最初我被卡在@Secured注释被忽略了,现在我已经解决了,我被困在拒绝访问。
感觉我的问题听起来非常类似于:@secured with granted authorities throws access denied exception - 但我仍然被拒绝访问。
这是我的设置:
弹簧security.xml文件
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder ref="passwordEncoder" />
</authentication-provider>
</authentication-manager>
<beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.PlaintextPasswordEncoder"/>
<user-service id="userDetailsService">
<user name="john" password="john1" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="jane" password="jane1" authorities="ROLE_USER" />
<user name="apiuser" password="apiuser" authorities="PERMISSION_TEST" />
</user-service>
控制器:
@Controller
@RequestMapping("/secure")
public class SecureController
{
private static final Logger logger = Logger.getLogger(SecureController.class);
@Secured("PERMISSION_TEST")
@RequestMapping(value = "/makeRequest", method = RequestMethod.GET)
@ResponseBody
public SimpleDTO executeSecureCall()
{
logger.debug("[executeSecureCall] Received request to a secure method");
SimpleDTO dto = new SimpleDTO();
dto.setStringVariable("You are authorized!");
return dto;
}
}
现在 - 没有正确的
<security:global-method-security secured-annotations="enabled"/>
我的请求通过(这是因为@Secured注释被忽略)。当我把它放入并使用“apiuser”/“apiuser”访问它时,我一直拒绝访问,调试日志:
11:42:43,899 [http-apr-8080-exec-4] DEBUG MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@cc12af5d: Principal: org.springframework.security.core.userdetails.User@d059c8e5: Username: apiuser; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: PERMISSION_TEST; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: PERMISSION_TEST
11:42:43,899 [http-apr-8080-exec-4] DEBUG AffirmativeBased - Voter: org.springframework.security.access.vote.RoleVoter@2a9a42ef, returned: 0
11:42:43,900 [http-apr-8080-exec-4] DEBUG AffirmativeBased - Voter: org.springframework.security.access.vote.AuthenticatedVoter@75a06ec2, returned: 0
11:42:43,902 [http-apr-8080-exec-4] DEBUG AnnotationMethodHandlerExceptionResolver - Resolving exception from handler [com.test.webapp.spring.controller.SecureController@342d150f]: org.springframework.security.access.AccessDeniedException: Access is denied
11:42:43,905 [http-apr-8080-exec-4] DEBUG ResponseStatusExceptionResolver - Resolving exception from handler [com.test.webapp.spring.controller.SecureController@342d150f]: org.springframework.security.access.AccessDeniedException: Access is denied
11:42:43,906 [http-apr-8080-exec-4] DEBUG DefaultHandlerExceptionResolver - Resolving exception from handler [com.test.webapp.spring.controller.SecureController@342d150f]: org.springframework.security.access.AccessDeniedException: Access is denied
11:42:43,909 [http-apr-8080-exec-4] DEBUG DispatcherServlet - Could not complete request
org.springframework.security.access.AccessDeniedException: Access is denied
思想?
提前致谢!
答案 0 :(得分:23)
我记得@Secured
注释仅适用于默认情况下以ROLE_
开头的角色名称。
您可以切换为@PreAuthorize("hasAuthority('PERMISSION_TEST')")
(使用pre-post-annotations="enabled"
)或重命名您的角色。
答案 1 :(得分:8)
我想再向Michail Nikolaev补充一点。 我的答案来自源代码的观点。我希望你理解访问被拒绝的原因。
来自文档:
当您使用命名空间配置时,会自动为您注册一个默认的AccessDecisionManager实例,并将根据您在拦截网址中指定的访问属性,使用它来为方法调用和Web URL访问做出访问决策。 -pointcut声明(如果使用注释安全方法,则在注释中)。 默认策略是使用具有RoleVoter和AuthenticatedVoter的AffirmativeBased AccessDecisionManager。
RoleVoter
使用ROLE_
前缀(默认情况下)以决定是否可以投票。您可以使用RoleVoter.setRolePrefix()
方法更改该默认前缀。
来自源代码:
public class RoleVoter implements AccessDecisionVoter<Object> {
(...)
private String rolePrefix = "ROLE_";
(...)
public void setRolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
}
(...)
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null) &&
attribute.getAttribute().startsWith(getRolePrefix())) {
return true;
} else {
return false;
}
}
(...)
public int vote(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes) {
int result = ACCESS_ABSTAIN;
Collection<? extends GrantedAuthority> authorities =
extractAuthorities(authentication);
for (ConfigAttribute attribute : attributes) {
if (this.supports(attribute)) {
result = ACCESS_DENIED;
// Attempt to find a matching granted authority
for (GrantedAuthority authority : authorities) {
if (attribute.getAttribute().equals(authority.getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
return result;
}
PERMISSION_TEST
不以ROLE_
开头,因此RoleVoter
弃权决定。 AuthenticatedVoter
也弃权(因为您未在IS_AUTHENTICATED_
注释中使用@Secured
前缀。
最后,AffirmativeBased
AccessDecisionManager
的实施引发了AccessDeniedException
,因为AccessDecisionVoters
都弃权了。
AffirmativeBased
的Java文档:
简单的具体实现 授予的org.springframework.security.access.AccessDecisionManager 访问,如果任何AccessDecisionVoter返回肯定响应。