领先一步
VMware 提供培训和认证,助您加速进步。
了解更多想象一下您正在一个安全的会话中(您已登录并被授权访问特定资源),但您的安全基础设施团队已更新了您的权限。也许您获得了更多权限,或者您的权限被完全撤销了……问题是您的安全会话已在会话注册表中注册,除非您注销/登录,否则代表您在此安全会话中的主体将不会被重新创建。如果情况更加严重呢(毕竟我们谈论的是安全)……您是一名心怀不满的员工,您的直接管理层发现了您的“不法行为”,但您的公司需要召开5次会议和10份审批表格才能完成某项工作,在此之前您可以自由地造成更大的伤害?
显然,有许多业务场景可能需要在用户处于会话中时调整其权限。同样在Spring Security中,显然有不止一种方法可以实现此目的。一种方法是使用会话注册表(实际上是用于并发会话控制的)将人们踢出系统。通过构建GUI或启用JMX,可以从已注册会话列表中“过期”用户。他们发出的下一个请求将被拒绝,并且会话将失效,从而强制执行身份验证过程。然而,为了使其更有趣,也为了演示实现类似目标的另一种方法,让我们稍微改变一下场景,假设我们不想完全使用户的安全会话失效,而只是想暂时挂起它。
现在,在安全上下文中,“挂起”这个词可能意味着许多不同的事情。它可能意味着暂时挂起,也可能意味着完全撤销权限。我将由您来定义这个词在任何上下文中的含义……我更愿意向您展示如何使用Spring Security组件(例如AccessDecisionVoter和AccessDesisionManager)实时调整安全会话。此外,在以下示例中,我们将通过暂时挂起用户的权限而不使其会话失效来调整当前安全会话。
要实现此目的,我们需要遵循以下步骤。
1. 在Spring Security配置中定义AccessDecisionManager 1.1 定义基本投票器,例如RoleVoter和AuthenticatedVoter 2. 定义并实现AccessDecisionVoter。此投票器必须维护一个已被撤销权限的用户列表,并且每次用户执行安全操作时都必须投票ACCESS_DENIED。 3. 将此投票器添加到已注册到AccessDecisionManager的投票器堆栈中。
此外,为了能够在应用程序运行时与此投票器交互,我们还将使用Spring JMX将其动态导出为JMX bean,从而启用JMX。
差不多就是这样。
所以,首先我们需要定义AccessDecisionManager (ADM)。由于我们使用Spring Security命名空间(只要我们能用),像"security:http"这样的配置元素,Spring Security将为我们注册默认的AccessDecisionManager,它恰好是AffirmativeBased ADM。这对我们不利,因为我们在这里试图保守一点。所以,我们要做的是定义UnanimousBased ADM,并使用指向UnanimousBased ADM的access-decision-manager-ref属性覆盖"security:http"元素的默认ADM(见下文)
<!-- Define custom Voter --> <bean id="suspendVoter" class="org.springframework.security.sample.SuspendRealTimeVoter"/> <!-- Define AccessDesisionManager as UnanimousBased --> <bean id="accessDecisionManager" class="org.springframework.security.vote.UnanimousBased"> <property name="decisionVoters"> <list> <ref bean="suspendVoter" /> <bean class="org.springframework.security.vote.RoleVoter" /> <bean class="org.springframework.security.vote.AuthenticatedVoter" /> </list> </property> </bean>我们还将把我们的自定义投票器放在投票器堆栈中的第一位,因为我们知道在基于一致的决策制定过程中,第一个“否”意味着不应执行进一步的评估。考虑到其他投票决策可能耗时或耗费性能,将自定义投票器首先注册到投票器堆栈中将确保只有在权限未暂停的情况下才执行此类投票操作。
<!-- 定义http安全配置 --> <security:http access-decision-manager-ref="accessDecisionManager" . . . . .> . . . . </security:http>
剩下唯一要做的事情就是启用“suspendVoter”的JMX功能。这可以使用Spring JMX轻松完成。 <!-- 启用自定义投票器的JMX --> <bean class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <map> <entry key="org.springframework.security:name=SuspendRealTimeVoter" value-ref="suspendVoter" /> </map> </property> </bean>
现在,"suspendVoter"将以org.springframework.security:name=SuspendRealTimeVoter的名称导出到JMX服务器中
从代码的角度来看,我们需要实现的唯一自定义组件是SuspendRealTimeVoter类。在该类中,我们将维护一个已暂时撤销权限的用户集合。在vote(..)方法内部,我们的逻辑相当简单。如果用户在列表中,则投票ACCESS_DENIED,否则投票ACCESS_GRANTED。
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) { String userName = authentication.getName(); return revokedUsers.contains(userName) ? ACCESS_DENIED : ACCESS_GRANTED; }
您还将在其中看到suspend(String userName)和grant(String userName)方法,我们将通过它们测试此功能。
注意:要通过JMX与我们的投票器交互,我们将使用JDK (1.5+)提供的JConsole工具,因此需要为我们的应用服务器启用JMX。对于此示例,我正在使用Tomcat,所以如果您正在使用Tomcat,请在其启动脚本中添加以下VM参数
-Dcom.sun.management.jmxremote
启动服务器,部署应用程序并访问其URL:https://:8080/spring-security-sample-suspendUser
成功登录后,点击“刷新”几次,查看您的会话是否正常运行。然后导航到JDK的bin目录,打开命令提示符并输入jconsole(见下文)
当JConsole打开时,点击MBeans选项卡。导航树并访问SuspendRealTimeVoter。
您将看到suspend(..)和grant(..)方法在Operations选项卡下可用。挂起您登录的用户的权限,并刷新您所在的HTML页面。您将看到denied.html页面。通过为该用户调用grant(..)方法来取消挂起用户,您将恢复正常。现在,如果确实需要5次会议和10份批准表格,您可以暂时撤销用户的权限,给您的经理足够的时间做出此类决定。
结论
这只是一个示例,演示了如何使用投票器暂时挂起安全会话。但我希望您能清楚地看到相同的Dota如何用于实现其他目标。这些目标可以是自动重新验证用户,或者只是更新他/她的GrantedAuthorities列表等等。
本文的示例源代码可在此处下载:spring-security-sample-suspenduser