I've followed quite a few threads to implement Spring Security to my rest API. Initially I get stuck at @Secured annotation being ignored, now that I got that resolved, I am stuck at getting access denied.
Feels like my problem sound very similar to: @secured with granted authorities throws access denied exception - but I am still getting access denied.
Here's my setup:
spring-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:
@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;
}
}
Now - without the proper
<security:global-method-security secured-annotations="enabled"/>
My request goes through (this is because the @Secured annotation is ignored). When I put it in and accessing it using "apiuser"/"apiuser", I kept getting access denied, the debug log:
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
Thoughts?
Thanks in advance!
As I remember @Secured
annotation works only with role names starting ROLE_
by default.
You may switch to @PreAuthorize("hasAuthority('PERMISSION_TEST')")
(with pre-post-annotations="enabled"
) or rename your role.
I want to add a little more to Michail Nikolaev answer. My answer is from the source code point of view. I want you to understand why access was denied.
From documentation:
When you use a namespace configuration, a default instance of AccessDecisionManager is automatically registered for you and will be used for making access decisions for method invocations and web URL access, based on the access attributes you specify in your intercept-url and protect-pointcut declarations (and in annotations if you are using annotation secured methods). The default strategy is to use an AffirmativeBased AccessDecisionManager with a RoleVoter and an AuthenticatedVoter.
RoleVoter
uses ROLE_
prefix (by default) in order to decide if it can vote. You can change that default prefix with RoleVoter.setRolePrefix()
method.
From source code:
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
doesn't start with ROLE_
so RoleVoter
abstains from deciding. AuthenticatedVoter
abstains too (as you have not used IS_AUTHENTICATED_
prefix in @Secured
annotation).
Finally, AffirmativeBased
implementation of AccessDecisionManager
throws AccessDeniedException
because both AccessDecisionVoters
abstained from voting.
Java docs for AffirmativeBased
:
Simple concrete implementation of org.springframework.security.access.AccessDecisionManager that grants access if any AccessDecisionVoter returns an affirmative response.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With