领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多假设您处于安全会话中(您已登录并被授权访问特定资源),但您的安全基础架构团队已更新您的权限和特权。也许您获得了更多权限和特权,或者您的权限被完全撤销了……问题在于您的安全会话已在会话注册表中注册,并且在您注销/登录之前,代表您在此安全会话中的 Principal 不会被重新创建。如果情况更加严峻(毕竟我们正在讨论安全问题)……您是一名心怀不满的员工,您的直接管理层发现了您的“不当行为”,但您的公司需要 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 并使用指向UninimousBased ADM 的access-decision-manager-ref 属性覆盖“security:http”元素的默认 ADM(见下文)
<!-- 定义自定义投票者 --> <bean id="suspendVoter" class="org.springframework.security.sample.SuspendRealTimeVoter"/>
<!-- 将 AccessDesisionManager 定义为 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>
我们还将把自定义投票者放在投票者堆栈顺序的第一位,因为在基于一致性的决策过程中,第一个 NO 意味着不应执行进一步的评估。意识到其他投票决策可能很耗时或性能密集,在投票者堆栈中首先注册自定义投票者将确保仅在未挂起权限时才执行此类投票操作。
<!-- 定义 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”将导出到 JMX 服务器,名称为org.springframework.security:name=SuspendRealTimeVoter
从代码角度来看,我们唯一需要实现的自定义组件是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://127.0.0.1:8080/spring-security-sample-suspendUser
成功登录后,单击刷新几次以查看您的会话是否处于活动状态并且运行良好。然后导航到 JDK 的bin目录,打开命令提示符并键入jconsole(见下文)
当 JConsole 打开时,单击 MBeans 选项卡。导航树并访问 SuspendRealTimeVoter。
您将看到suspend(..)和grant(..)方法在操作选项卡下可用。挂起您登录的用户权限,然后刷新您所在的 HTML 页面。您将看到 denied.html 页面。通过对该用户调用grant(..)方法来取消挂起用户,您将恢复工作。现在,如果确实需要 5 次会议和 10 份审批表,您可以暂时撤销用户的权限,从而为您的管理人员提供足够的时间来做出此类决定。
结论
这只是一个示例,演示了如何使用投票者暂时挂起安全会话。但我希望您能清楚地看到如何使用相同的方法来实现其他目标。此类目标可能是自动重新验证用户或简单地更新他/她的 GrantedAuorities 列表等等。
本文的示例源代码可以从此处下载:spring-security-sample-suspenduser