Java spring-security 访问被拒绝异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22666416/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:11:09  来源:igfitidea点击:

spring-security access denied exception

javaspringspring-security

提问by Jonas Geiregat

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

我在使用 spring-security 的 @secured 注释的方法有问题

@Secured(value = { "LIST_GIFT" })

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

该方法已放置在 spring-mvc Web 应用程序的 Controller 方法上。

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

当我通过 UserDetailsS​​ervice 实现进行调试时,我们在 loadByUserName 方法的末尾有以下 UserDetails 对象:

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:

HTTP 总是返回 403 Forbidden。当我打开日志记录时,我看到以下内容:

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.

我不知道为什么会发生这种情况。

采纳答案by Shaun the Sheep

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.

看起来AccessDecisionVoter您拥有的所有s 都没有投票授予访问权限(他们都弃权),因此您被拒绝访问。

The reason for this is that, by default, the RoleVoterwhich 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.

这样做的原因是,默认情况下,作为RoleVoter默认配置的一部分,只会响应以前缀“ROLE_”开头的权限。因为你的都没有,所以它找不到任何它知道的东西。此常见问题解答包含更多信息

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

另一种方法是启用基于表达式的安全性并改用@PreAuthorize注释,这样更灵活。就像是

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

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

如果您按照描述启用注释,则应该可以立即使用。

回答by EliuX

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

问题在于,当您GrantedAuthority在会话中加载 User元素时,您应该对它们进行不同的命名,以区分用户配置文件的权限。

  • ROLEprefix for User profiles
  • PERMprefix for User permissions
  • ROLE用户配置文件的前缀
  • PERM用户权限前缀

So you should name your permission as PERM_LIST_GIFTand use the hasAuthorityannotation 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:

因此,您应该将您的许可命名为 asPERM_LIST_GIFT并改用hasAuthority注释。我还建议将权限作为 CRUD ;因此,要列出礼物,您将有权阅读礼物:PERM_READ_GIFT。并且为了改进这种访问,您可以进行自定义注释以指示某人有权访问(至少是只读)您的礼物对象。就像是:

/**
 * 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.

因此,您可以使用此注释所有功能,并且如果策略更改为访问您的礼物元素,您只需更改一次。它更好,更容易处理。