从 Grails 中的过滤器定义控制器可访问变量

Posted

技术标签:

【中文标题】从 Grails 中的过滤器定义控制器可访问变量【英文标题】:Defining controller accessible variables from filters in Grails 【发布时间】:2010-10-04 09:10:30 【问题描述】:

我正在 Grails 中编写一个小型 Web 应用程序,为了确保所有用户都经过身份验证,我使用了以下过滤器:

class LoginFilters 
  static filters = 
    loginCheck(controller:'*', action:'*') 
      before = 
        if (session.user_id) 
          request.user = User.get(session.user_id)
         else if (!actionName.equals("login")) 
          redirect(controller: "login", action: "login")
          return false
        
      
    
  

而所有控制器方法都是从读取请求对象的用户属性开始的:

def actionName = 
   def user = request.user
   ...

上面的代码有效,但我宁愿避免所有控制器方法中的重复代码。过滤器是否可以将用户对象绑定到一个名为“user”而不是“request.user”的变量,所有控制器都可以访问该变量?

我知道可能存在范围界定问题导致这不可能,但 Grails 框架似乎能够在幕后创造相当多的魔力,所以我认为这可能值得一问。

【问题讨论】:

【参考方案1】:

在控制器中使用 beforeInterceptor 可能会有所帮助:

class LoginController 

    def user

    def beforeInterceptor = 
        user = request.user
    

    def index =  
        render text:"index: $user"        
    

    def test = 
        render text:"test: $user"         
    

【讨论】:

【参考方案2】:

我认为每次都将用户对象插入到请求对象中通常不是一个好主意:

请求的生命周期非常短,因此您最终可能会在每个 http-request 中往返于缓存,甚至更糟地访问数据库以检索对象,您甚至可能不需要这些对象,并且之后会立即将其删除。因此,如果必须,最好将整个对象存储在会话中,而不仅仅是 id。

一般来说,我建议您编写一个 AuthenticationService,其中包含在用户通过身份验证时返回 true 的方法 isLoggedIn() 和返回此对象的方法 getLoggedInUser()

class AuthenticationService 
    def transactional = false
    boolean isLoggedIn()  return session.user_id 
    def getLoggedInUser()  return User.get(session.user_id) 

然后,如果未通过身份验证,则使用过滤器进行重定向,并且可能使用拦截器来存储本地引用 user = authenticationService.loggedInUser。但我也不认为这是最好的方法。我建议您为 src/groovy 中的所有控制器创建一个抽象的 AuthenticationAwareController 作为基类,并且有像 user 这样的便捷方法

class AuthenticationAwareController 
    def authenticationService
    def getUser()  return authenticationService.loggedInUser() 

这样,您以后可以改变主意,按照自己的喜好存储用户,而不必更改代码。此外,您还受益于 Hibernate 中的缓存,它在不同会话之间共享已检索到的用户对象实例,因此避免了数据库往返。

您仍然应该检查检索到的用户对象的有效性或抛出AuthenticationException,以防检索不成功。 (可能类似于AuthenticationService.getLoggedInUser(failOnError = false)。)

您甚至可以将此 Service/ControllerBase 制作为一个小插件,以便在每个应用程序上重复使用,或者直接使用 spring 安全插件... ;-)

【讨论】:

【参考方案3】:

我认为你可以做到这一点,但这真的值得吗?在我看来,您唯一的优势是输入“user”而不是“request.user”。收获不大。无论如何,我认为您可以按照用户指南的“12.7 在运行时添加动态方法”中的说明进行操作。我认为如果您创建了一个动态方法“getUser() return request.user”,Groovy JavaBeans getter/setter 访问将允许您以您想要的方式简单地引用“用户”。

如果您确实添加了动态方法,您可能希望跳过过滤器并在动态方法中完成所有操作。

【讨论】:

以上是关于从 Grails 中的过滤器定义控制器可访问变量的主要内容,如果未能解决你的问题,请参考以下文章

Grails 重定向控制器从 https 切换到 http。为啥?

如何从 Grails 中的控制器访问域属性?

如何在 Grails 2 过滤器中定义多个不同的控制器?

如何保护 Grails 中的所有 REST 请求和响应

Grails 不会将变量传递给嵌套模板

如何在 grails 控制器中使用路径变量?