Search code examples
sessiongrailsshiro

How to store/load username in Grails app?


I have a Grails (2.3.6) app that requires users to login before they can access the app. As soon as they login successfully, they are redirected to a landing page that displays their username in the top-right corner. I had this working nicely (or so I thought) with the following 3 pieces of code:

// myapp/grails-app/realms/myapp/MyAppRealm.groovy:
class MyAppRealm {
    def onSuccessfulLogin(Principal principal) {
        // Lots of code

        // If I logged in successfully with a username of "user1", then principal.name will be "user1" here.
        UserHolder.name = principal.name

        // Lots more code
    }
}

In the above code the onSuccessfulLogin() method is executed if the authentication layer determines they logged in successfully. The principal.name value is the authenticated username they logged in with. As you can see I store this value in a UserHolder POGO which looks like:

class UserHolder {
    static String name = null

    static String getName() {
        name
    }

    static void setName(String n) {
        name = n
    }
}

Then, in my GSP file:

// myapp/grails-app/views/page.gsp
You are logged in as ${UserHolder?.name}.

It was just reported to me that a tester logged in successfully, and a few minutes later, hit F5 and refreshed their screen, only to find that their username belonged to another tester! After looking at the code, the reason for this is obvious: UserHolder.name is a static variable; only 1 instance of it will live on the server at any given point in time. So if user1 logs in, they'll see user1 as their username in the UI. But if user2 then logs in, User.name now equals user2, and if user1 refreshes their screen, they'll see user2 as their username.

So my next thought was to try and store principal.name in a session variable like session.name = principal.name:

// myapp/grails-app/realms/myapp/MyAppRealm.groovy:
class MyAppRealm {
    def onSuccessfulLogin(Principal principal) {
        // Lots of code

        // If I logged in successfully with a username of "user1", then principal.name will be "user1" here.
        session.name = principal.name

        // Lots more code
    }
}

...and then refer to it in the GSP like You are logged in as ${session.name}.. The problem there is that I don't have access to session from inside MyAppRealm.groovy, and it's not easily injectable (unless anyone can think of how). So I ask: how can I "record" principal.name over in MyAppRealm, and then specify the correct username from inside the GSP?


Solution

  • I had a similar requirement in my Groovy and Grails app.Not really a direct answer to your solution but you can try something similar. This is what I did:

    class UserController {
    
    def register() {
        if(request.method == 'POST') {
            def u = new User()
            u.properties[
                        'login',
                        'password',
                        'firstName',
                        'lastName',
                        ] = params
    
             if(u.save()) {
                session.user = u
                session.setMaxInactiveInterval(-1)
                redirect(controller: "myController", action: "welcome")
                    }
    }
    

    And in my view, I used it as follows:

    Welcome ${session?.user?.firstName}
    

    My User domain class is as follows:

    class User {
        String login
        String password
        String firstName
        String lastName
    }
    

    Hope it helps :)