为了账号安全,请及时绑定邮箱和手机立即绑定

尽管已授予权限,Spring Security 仍拒绝访问“@Secured”方法

尽管已授予权限,Spring Security 仍拒绝访问“@Secured”方法

森林海 2023-01-05 10:02:50

所以我有一个@Secure为 Spring Security 设计的 Vaadin (8) 视图:


@Secured(SUPERADMIN_ROLE)

@SpringView(name = AdminHomeView.NAME)

class AdminHomeView : DemoViewWithLabel(){

    companion object {

        const val NAME = "admin/home"

    }


    override val labelContent = "This is the protected admin section. You are authenticated and authorized."

}

哪里DemoViewWithLabel只是一个非常简单的抽象类显示


VerticalLayout(Label(labelContent))

因此,如果我以具有该角色的身份登录Superadmin,我就可以很好地访问该视图。


但是,让我们做一个小改动并覆盖一个方法......


@Secured(SUPERADMIN_ROLE)

@SpringView(name = AdminHomeView.NAME)

class AdminHomeView : DemoViewWithLabel(){

    companion object {

        const val NAME = "admin/home"

    }


    override val labelContent = "This is the protected admin section. You are authenticated and authorized."


    override fun enter(event: ViewChangeListener.ViewChangeEvent?) {

        super.enter(event)

    }

}

这让我AccessDeniedException……我不明白为什么。


所以我打开了 Spring Security 的 debug loggign,这就是它必须说的:


Secure object: ReflectiveMethodInvocation: public void ch.cypherk.myapp.ui.views.admin.AdminHomeView.enter(com.vaadin.navigator.ViewChangeListener$ViewChangeEvent);

  target is of class [ch.cypherk.myapp.ui.views.admin.AdminHomeView];

  Attributes: [Superadmin]

Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a6801701:

  Principal: ch.cypherk.myapp.model.auth.MyUserDetails@6e4c5c5b;

  Credentials: [PROTECTED];

  Authenticated: true; Details: null;

  Granted Authorities: RIGHT_MANAGER, Superadmin 

到目前为止,这似乎还可以。它需要一个Superadmin权限,并且它有一个具有该Superadmin权限的经过身份验证的用户。


查看完整描述

1 回答

?
梵蒂冈之花

TA贡献1618条经验 获得超5个赞

简而言之


授予的权限不会显示ROLE_-prefix,因此会被RoleVoter.


将 替换为RoleVoter具有空前缀的自定义实现解决了该问题。


全文


现在这留下了为什么我们可以访问视图的问题。解释很简单,但需要更多的上下文。


我们正在努力将 Thorntail 应用程序迁移到 Spring Boot。


我们正在使用 Vaadin 8(因为我们有遗留的 Vaadin 7 东西,我们还没有设法摆脱它,需要支持)。


所以?


Vaadin 是一个单页框架,Vaadin 8 有这种讨厌的视图引用方式,即它使用#!根 url(例如https://<host>:<port>/#!foo/bar/baz/...)。


之后什么#都没有发送到服务器,这意味着我们无法区分访问/和访问/#!foo/bar/baz/...


因此,我们不能使用 Spring Security 来保护对视图的访问。


并且我们有一个 Vaadin 视图,我们需要允许未经身份验证的访问。


因此,我们被迫允许对/. (MainUI处理所有传入请求的)将检查用户是否已通过身份验证,如果未通过身份验证,则重定向到登录页面。


对视图本身的访问由 Vaadin 保护,而不是 Spring。将SpringViewProvider找不到View用户无权访问的内容(因此,用户无法导航到那里)。


但是,只要我们在该视图上调用方法,Spring 安全性就会介入并执行访问检查。正是这些检查失败了,因为授予的权限没有 Spring 预期的“ROLE_”前缀。(我原以为这只是一个约定,但事实并非如此;RoleVoter实际上忽略了不显示前缀的权限,因此您必须那样做,或者您必须提供自己的RoleVoter.)


解决方案相对简单......


@Configuration

@EnableGlobalMethodSecurity(

    securedEnabled = true,

    prePostEnabled = true,

    proxyTargetClass = true

)

class GlobalSecurityConfiguration : GlobalMethodSecurityConfiguration(){

    override fun accessDecisionManager(): AccessDecisionManager {

        return (super.accessDecisionManager() as AffirmativeBased).apply {

            decisionVoters.replaceAll {

                when(it){

                    is RoleVoter -> MyRoleVoter()

                    else -> it

                }

            }

        }

    }

}

在哪里


class MyRoleVoter : RoleVoter(){

    init {

        rolePrefix = ""

    }

}


查看完整回答
反对 回复 2023-01-05

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信