本小节实现登录功能。在本小节,读者还将学习到 Filter 的用法和 uitls 的用法。

首先创建用于登录的 Domain Class,在控制台输入:
grails create-domain-class Users
然后修改 Domain Class 的内容如下:
class Users { 
    String username
    String password
    static constraints = {
        username(size:5..60,blank:false,unique:true)
        password(size:5..60,blank:false)
    }
}
相信读者一定不希望将密码的名文直接保存到数据库中。这里就涉及一个 SHA 的加密过程,Grails 希望我们在 utils 中实现这个过程。在 grails-app\utils 中创建一个名为 PasswordCodec 的 Groovy 类(强调一下类名必需是 XXXCodec),编写代码如下:
import java.security.MessageDigest
import sun.misc.BASE64Encoder
import sun.misc.CharacterEncoder

class PasswordCodec {
    static encode = { str ->
        MessageDigest md = MessageDigest.getInstance('SHA')
        md.update(str.getBytes('UTF-8'))
        return (new BASE64Encoder()).encode(md.digest())
    }
}
接下来我们写程序让应用启动时自动往 Users 表添加一条记录。BootStrap 类可以帮助我们实现这个功能,修改 grails-app\config\BootStrap.groovy 的代码:
class BootStrap {
    def init = { servletContext ->
        def user = new Users(id:1,username:'liangshixing'
            ,password:'liangshixing')
        user.password = user.password.encodeAsPassword()
        user.save()
    }
    def destroy = { }
}
BootStrap 类中的 init 闭包中的代码将在启动时首先执行。这里我们创建了一个 Users 对象,并为它初始化了用户名和密码。

这里要注意一点,user.password.encodeAsPassword() 正是源于我们创建的 Utils 类,Grails 会根据命名约定,查找并执行 PasswordCodec 类的 encode 闭包,从而实现了 SHA 的加密。

Utils 类也是 Grails 的约定编码的一个典泛。

接下来实现表单页面和 Controller(可以先使用 generate-all,然后在自动生成的代码上改),这里仅摘录 Controller 的代码:
class UsersController {
    def index = { redirect(action:login,params:params) }
    def login = {
        def users = new Users()
        users.properties = params
        return ['users':users]
    }
    def loginCheck = {
        def users = new Users(params)
        if(!users.hasErrors()) {
            users.password = params.password.encodeAsPassword()
            def resultUsers=Users.findByUsernameAndPassword(users.username,users.password)
            if(resultUsers) {
                session.user = resultUsers
                redirect(controller:'member',action:'search')
            } else {
                flash.message = 'Login failed: incorrect username and password'
                render(view:'login',model:[users:users])
            }        
        } else {
            render(view:'login',model:[users:users])
        }
    }
    def logOut = {
        session.invalidate()
        redirect(controller:'users',action:'login')
    }
}
login Action 会跳转到 login.gsp,用于显示登录的表单页面。

loginCheckAction 将检查用户名密码是否正确。如果正确,则将 Users 对象保存到 session 中,然后重定向到搜索 member 的页面;如果不正确,报错并要求重新登录。

logOut 调用 session 的 invalidate 方法,可以清空 session。然后调用 redirect 从定向到登录页面。

登录登出的功能已经实现了,但我们的应用还没有被保护起来,Filter 可以帮助实现这个功能(Grails1.0rc1 中第一次支持这个功能)。

首先在 grails-app\config\ 文件夹中创建一个名为 LoginFilters 的类(类名必须以 Filters 结尾)。编写如下代码:
class LoginFilters {
    def filters = {
        loginCheck(controller:'*', action:'*') {
            before = {
                if(!session.user) {
                     if(!actionName.equals('login') 
                         && !actionName.equals('logOut') 
                        && !actionName.equals('loginCheck') 
                        && !actionName.equals('list') 
                        && !actionName.equals('search') 
                        && !actionName.equals('show')) {
                            redirect(controller:'users',action:'login')
                            return false
                    }
                }
            }
        }
    }
}
Filters 类中可以使用 actionName 和 controllerName 去获取 action 和 controller 的名称。本文例子中的限制只有 login,logOut,loginCheck,list,search,show 不需要登录,如果访问其它页面,都需要登录。
快乐渡过每一天,减肥坚持每一天