在客户端或服务器端拦截 Grails GSP 操作

Intercepting Grails GSP actions on either client or server side

此处为 Grails 2.4.5。我正在尝试为我的 GSP 实现以下 UX 行为:

为了确定用户是否拥有所需的权限,我可以访问 Groovy 和 GSP/taglib 层的功能。

来自Groovy/controller层:

SecurityUtils.hasPermission(String permission)
Ex: SecurityUtils.hasPermission('UPDATE_BUZZ')

来自GSP/taglib层:

<sec:hasPermission permission="<permission name>">???</sec:hasPermission>
Ex: <sec:hasPermission permission="UPDATE_BUZZ">???</sec:hasPermission>

因此,给定这两个可用的访问检查机制,并给定以下控制器:

class FizzController {
    BuzzService BuzzService

    def buzz() {
        SomeData dataModel = buzzService.getModel(params)
        render(view: 'buzz', model: [ dataModel: dataModel ])
    }
}

...其中 buzz.gsp 是:

<!-- Lots of HTML/GSP here -->
<g:submitButton name="update" value="Update" />
<!-- Lots more HTML/GSP down here -->

考虑到所有这些,我的问题是:How/where 我应该:(1) 响应“update”按钮的点击处理程序,(2) 执行访问检查, (3) 呈现 error/banner/flash 消息? 代码示例(即使是伪代码)也很棒!

最好的方法是在用户无权使用按钮时隐藏该按钮。这可以通过 <sec:hasPermission permission="UPDATE_BUZZ">button</sec:hasPermission>

轻松实现

如果您希望即使未经许可的用户也能显示按钮,您可以使用相同的 hasPermission 向按钮添加额外的 class。现在使用 jQuery 捕获 class 的时钟事件并显示您的消息。

以下是我的建议:

  1. 确保您的控制器方法是基于角色的
  2. 取消 GSP 中的权限检查,如果按钮应该对所有人可见
  3. 提交后创建一个AJAX调用,如果响应状态为403,则显示横幅。

根据你的问题,我假设你不想刷新页面,甚至可能不想调用 ajax。因为如果你这样做了,那么展示横幅就不难了。您只希望它表现得像 JavaScript client-side 验证(UX 明智)。如果这个假设是错误的,那么不要阅读和使用 Aramiti 的解决方案。否则继续。

第一个解

您可以创建一个将权限作为输入的标签。像

<myTagLib:flashOnNoPermission permission="PERM" name="name" value="value">
</myTagLib:flashOnNoPermission>

这个标签的定义可以使用sec:hasPermission检查权限。然后这个标签就可以呈现一个包含类似这样的东西的模板

<hidden flash message>
<g:submitButton name="name" value="value" onclick="<unhide flash if no permission>"/>

基本上是在 grails 按钮上创建一个包装器,这样您就可以在按钮上显示即时消息。

问题

  • 如果用户在屏幕上时更改了用户权限怎么办? Ajax 会解决这个问题,但这不会。加载屏幕后,一切都已修复。
  • 横幅与按钮一起

第二种解法

在布局顶部添加一个通用 div 以显示即显消息。然后创建一个类似于上面的标签。只是不要在呈现的模板中添加 flash 消息。像

    <g:submitButton name="name" value="value" onclick="<unhide flash at top of layout if no permission>"/>

问题

  • 如果用户在屏幕上时更改了权限怎么办?

不确定你为什么需要 onclick 处理程序,但如果没有理由只是 Aramiti 的解决方案。

如果我是你,我可能会尝试在这种情况下使用 before-action 过滤器。在此过滤器中,我将检查当前用户是否有权执行此类操作(检查权限应始终在 server-side 中完成,出于安全原因)并且:

  • 如果安全检查通过 - 只是 return true(控制器将继续它的流程)

  • 如果安全检查失败 - 您可以使用默认 flash.message 和 return false(使用适当的 css 样式闪现消息可能会出现在屏幕顶部具有 rose/pinkish/red 背景)

示例代码:

class UserFilters {
   def filters = {
      // ... other filters ...
      permissionAllCheck(controller: '*', action: '*') {
         before = {
            doPermissionCheck(delegate)
         }
      }
   }

   private boolean doPermissionCheck(filters) {
         if (! YourService.checkForPermissionForCurrentlyLoggedUser()) {
            filters.flash.message = "You don't have permission to take this action"
            return false
         }
      true
   }
}

如果您只想对特定 controller/action 使用过滤器,请选中 applying section. Remember also, that you can use invert rule filter

有关不同 filterTypes 的更多信息。

您还应该记得将 flash.message 部分添加到您的布局(或选定的视图)中。您始终可以指定 type of flash 消息,它是 css 样式。