Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spring-security access denied exception

I'm having an issue with a method annotated with @secured of spring-security

@Secured(value = { "LIST_GIFT" })

The method has been placed on a Controller method of a spring-mvc web application.

When I debug through the UserDetailsService Implementation we have at the end of the loadByUserName method I have the following UserDetails object:

SecurityDetails [userId=106, [email protected], enabled=true, accountNonLocked=true, accountNonExpired=true, credentialsNonExpired=true]

With the following granted authoroties list:

[LOCK_SERIAL, CALCULATE_BALANCE, REGISTER_FOR_PUSH_NOTIFICATION, TOPUP_BY_POS, LIST_PRODUCTS, LIST_COUPON_ASSIGNMENT, BALANCE, BALANCE_HISTORY, LIST_GIFT, LIST_PROGRAMS, ASSIGN_COUPON, DISTRIBUTE_COUPON, CAN_CONFIGURE_GIFTS]

The HTTP always return 403 Forbidden. When I turn on logging I see the following:

DEBUG: org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public com.foo.products.sva.cc.customer.domain.AccountBalance com.foo.products.sva.cc.customer.controllers.AccountController.getBalance(java.lang.String); target is of class [com.foo.products.sva.cc.customer.controllers.AccountController]; Attributes: [LIST_GIFT]
    DEBUG: org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@380cef3f: Principal: SecurityDetails [userId=106, [email protected], enabled=true, accountNonLocked=true, accountNonExpired=true, credentialsNonExpired=true]; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffed504: RemoteIpAddress: 127.0.0.1; SessionId: F5EAAB76CD6CC8530F3E7E844A13D635; Granted Authorities: CALCULATE_BALANCE, REGISTER_FOR_PUSH_NOTIFICATION, TOPUP_BY_POS, CAN_CONFIGURE_GIFTS, DISTRIBUTE_COUPON, ASSIGN_COUPON, LIST_COUPON_ASSIGNMENT, LIST_GIFT, BALANCE_HISTORY, LIST_PRODUCTS, LOCK_SERIAL, LIST_PROGRAMS, BALANCE
    DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.access.vote.RoleVoter@2059dcd9, returned: 0
    DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.access.vote.AuthenticatedVoter@6bb23b26, returned: 0
    DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is not anonymous); delegating to AccessDeniedHandler
    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.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
            at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
            at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
            at com.foo.products.sva.cc.customer.controllers.AccountController$$EnhancerByCGLIB$$b72ae4b0.getBalance(<generated>)

I have no clue anymore on why this might be happening.

like image 734
Jonas Geiregat Avatar asked Oct 29 '25 09:10

Jonas Geiregat


2 Answers

It looks like none of the AccessDecisionVoters you have are voting to grant access (they are all abstaining) and hence you are being denied access.

The reason for this is that, by default, the RoleVoter which is part of the default configuration, will only respond to authorities which begin with the prefix "ROLE_". Since none of yours do, it doesn't find anything it knows about. This FAQ has some more information.

An alternative is to enable expression-based security and use the @PreAuthorize annotation instead, which is more flexible. Something like

@PreAuthorize("hasRole('LIST_GIFT')")

should work out of the box if you enable the annotations as described.

like image 86
Shaun the Sheep Avatar answered Oct 31 '25 02:10

Shaun the Sheep


The problem is that when you load your User GrantedAuthority elements in the session you should name them differently for differencing permission from user profiles.

  • ROLE prefix for User profiles
  • PERM prefix for User permissions

So you should name your permission as PERM_LIST_GIFT and use the hasAuthority annotation instead. I would also recommend to put the permissionas as a CRUD ; so for listing gifts you would have a permission to read gifts: PERM_READ_GIFT. And to improve this access, you could make a custom annotation for indicating somebody has permissions to access (at least for read only) your gifts objects. Something like:

/**
 * Grants permissions of accessing to Gifts module
 *
 */
@Target(
        {
            ElementType.TYPE, ElementType.METHOD
        })
@Component
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@PreAuthorize("hasAuthority('PERM_READ_GIFT')")
public @interface GiftsAuthorized
{

}

So you could annotate all functions with this, and if the policy changes to access to your gift elements, you would only have to change it once. Its better and easier to handle.

like image 23
EliuX Avatar answered Oct 31 '25 01:10

EliuX