Home Grails 3 SpringSecurity Google Login and Local Login
Reply: 0

Grails 3 SpringSecurity Google Login and Local Login

user13441
1#
user13441 Published in April 19, 2018, 10:03 am

I'm new to Grails 3. I'm trying to setup my security and I'm struggling to get everything to work. My goal is to have a local login form along with the google login button very similar to how SO does it.

I used the s2-quickstart com.example.project User Role to set up basic local security which appeared to work fine. I'm able to log in and out of my account with no issue.

Next I tried adding http://guides.grails.org/grails-oauth-google/guide/index.html If I remove the quickstart security code, I can get the google login to work somewhat. (Doesn't have roles or user data)

If I leave the quickstart code in, the google login doesn't actually log in. It pops up the page to select your username from google then does a redirect in my site but no login takes place.

The other thing I want it to do is create a database record using the data coming back from google, but I also can't seem to get the data beyond the google id let alone get it to save to the database.

My code.

application.groovy

// Added by the Spring Security Core plugin:

grails.plugin.springsecurity.userLookup.userDomainClassName = 'com.example.project.User'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'com.example.project.UserRole'
grails.plugin.springsecurity.authority.className = 'com.example.project.Role'
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
        [pattern: '/',               access: ['permitAll']],
        [pattern: '/error',          access: ['permitAll']],
        [pattern: '/index',          access: ['permitAll']],
        [pattern: '/index.gsp',      access: ['permitAll']],
        [pattern: '/shutdown',       access: ['permitAll']],
        [pattern: '/assets/**',      access: ['permitAll']],
        [pattern: '/**/js/**',       access: ['permitAll']],
        [pattern: '/**/css/**',      access: ['permitAll']],
        [pattern: '/**/images/**',   access: ['permitAll']],
        [pattern: '/**/favicon.ico', access: ['permitAll']]
]
//end::staticRules[]
//tag::grailsPluginSpringSecurityRest[]
grails {
    plugin {
        springsecurity {
            rest {
                token {
                    validation {
                        useBearerToken = false
                        enableAnonymousAccess = true
                    }
                    storage {
                        jwt {
                            secret = 'foobar123'*4
                        }
                    }
                }
                oauth {
                    frontendCallbackUrl = { String tokenValue -> "http://localhost:8080/auth/success?token=${tokenValue}" }
                    google {
                        client = org.pac4j.oauth.client.Google2Client 
                        key = 'key'
                        secret = 'secret' //<7>
                        scope = org.pac4j.oauth.client.Google2Client.Google2Scope.EMAIL_AND_PROFILE
                        defaultRoles = ['ROLE_USER']
                    }
                }
            }
            providerNames = ['anonymousAuthenticationProvider']

            successHandler {
                defaultTargetUrl = '/account'
            }

            userLookup {
              userDomainClassName = com.example.project.User
              authorityJoinClassName = com.example.project.UserRole
            }

            authority {
                className = com.example.project.Role
            }
        }
    }
}
//end::grailsPluginSpringSecurityRest[]

//tag::filterChain[]
String ANONYMOUS_FILTERS = 'anonymousAuthenticationFilter,restTokenValidationFilter,restExceptionTranslationFilter,filterInvocationInterceptor'
grails.plugin.springsecurity.filterChain.chainMap = [
        [pattern: '/dbconsole/**',      filters: 'none'],
        [pattern: '/assets/**',      filters: 'none'],
        [pattern: '/**/js/**',       filters: 'none'],
        [pattern: '/**/css/**',      filters: 'none'],
        [pattern: '/**/images/**',   filters: 'none'],
        [pattern: '/**/favicon.ico', filters: 'none'],
        //[pattern: '/', filters: ANONYMOUS_FILTERS],
        //[pattern: '/login', filters: ANONYMOUS_FILTERS],
        //[pattern: '/account/**', filters: ANONYMOUS_FILTERS],
        [pattern: '/auth/success', filters: ANONYMOUS_FILTERS],
        [pattern: '/oauth/authenticate/google', filters: ANONYMOUS_FILTERS],
        [pattern: '/oauth/callback/google', filters: ANONYMOUS_FILTERS],
        [pattern: '/**', filters: 'JOINED_FILTERS,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter'],
]
//end::filterChain[]

//tag::logoutHandlers[]
grails.plugin.springsecurity.logout.handlerNames = ['rememberMeServices', 'securityContextLogoutHandler', 'cookieClearingLogoutHandler']
//end::logoutHandlers[]

Resources.groovy

import com.example.project.UserPasswordEncoderListener
import com.example.project.JwtCookieTokenReader
//end::tokenReaderImport[]
//tag::cookieClearingImport[]
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler
//end::cookieClearingImport[]
beans = {
    userPasswordEncoderListener(UserPasswordEncoderListener)
    //tag::tokenReader[]
    tokenReader(JwtCookieTokenReader) {
        cookieName = 'jwt'
    }
    //end::tokenReader[]
    //tag::cookieClearing[]
    cookieClearingLogoutHandler(CookieClearingLogoutHandler, ['jwt'])
    //end::cookieClearing[]
}

AuthController

@Slf4j
class AuthController implements GrailsConfigurationAware {

    TokenReader tokenReader

    int jwtExpiration

    String grailsServerUrl

    static allowedMethods = [
            success: 'GET',
            logout: 'POST'
    ]

    @Secured('permitAll')
    def success(String token) {
        log.debug('token value {}', token)
        if (token) {
            Cookie cookie = jwtCookie(token)
            response.addCookie(cookie)
        }
        [grailsServerUrl: grailsServerUrl]
    }

    protected Cookie jwtCookie(String tokenValue) {
        Cookie jwtCookie = new Cookie( cookieName(), tokenValue )
        jwtCookie.maxAge = jwtExpiration
        jwtCookie.path = '/'
        jwtCookie.setHttpOnly(true)
        if ( httpOnly() ) {
            jwtCookie.setSecure(true)
        }
        jwtCookie
    }

    @Override
    void setConfiguration(Config co) {
        jwtExpiration = co.getProperty('grails.plugin.springsecurity.rest.token.storage.memcached.expiration', Integer, 3600)
        grailsServerUrl = co.getProperty('grails.serverURL', String)
    }

    protected boolean httpOnly() {
        grailsServerUrl?.startsWith('https')
    }

    protected String cookieName() {
        if ( tokenReader instanceof JwtCookieTokenReader ) {
            return ((JwtCookieTokenReader) tokenReader).cookieName
        }
        return 'jwt'
    }
}

Login.gsp

<form method="POST" action="${request.contextPath}/login/authenticate" class="form-signin">
    <h2 class="form-signin-heading">Please sign in</h2>
    <sec:ifLoggedIn>
        <g:form controller="logout" style="display: inline;">
            <input type="submit" value="${g.message(code: "logout", default:"Logout")}"/>
        </g:form>
    </sec:ifLoggedIn>

    <label for="username" class="sr-only">Email address</label>
    <input type="username" id="inputEmail" class="form-control" placeholder="Email address" required="" autofocus="" name="username">
    <label for="inputPassword" class="sr-only">Password</label>
    <input type="password" id="inputPassword" class="form-control" placeholder="Password" required="" name="password">
    <div class="checkbox">
        <label>
            <input type="checkbox" value="remember-me"> Remember me
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>

JwtCookieTokenReader

@Slf4j
@CompileStatic
class JwtCookieTokenReader implements TokenReader {

    final static String DEFAULT_COOKIE_NAME = 'JWT'

    String cookieName = DEFAULT_COOKIE_NAME

    @Override
    AccessToken findToken(HttpServletRequest request) {

        log.debug "Looking for jwt token in a cookie named {}", cookieName
        String tokenValue = null
        Cookie cookie = request.getCookies()?.find { Cookie cookie -> cookie.name.equalsIgnoreCase(cookieName) }

        if ( cookie ) {
            tokenValue = cookie.value
        }

        log.debug "Token: ${tokenValue}"
        return tokenValue ? new AccessToken(tokenValue) : null

    }
}

UserPasswordEncoderListener

@CompileStatic
class UserPasswordEncoderListener {

    @Autowired
    SpringSecurityService springSecurityService

    @Listener(User)
    void onPreInsertEvent(PreInsertEvent event) {
        encodePasswordForEvent(event)
    }

    @Listener(User)
    void onPreUpdateEvent(PreUpdateEvent event) {
        encodePasswordForEvent(event)
    }

    private void encodePasswordForEvent(AbstractPersistenceEvent event) {
        if (event.entityObject instanceof User) {
            User u = event.entityObject as User
            if (u.password && ((event instanceof  PreInsertEvent) || (event instanceof PreUpdateEvent && u.isDirty('password')))) {
                event.getEntityAccess().setProperty('password', encodePassword(u.password))
            }
        }
    }

    private String encodePassword(String password) {
        springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
    }
}

Role - domain

@GrailsCompileStatic
@EqualsAndHashCode(includes='authority')
@ToString(includes='authority', includeNames=true, includePackage=false)
class Role implements Serializable {

    private static final long serialVersionUID = 1

    String authority

    static constraints = {
        authority nullable: false, blank: false, unique: true
    }

    static mapping = {
        cache true
    }
}

User - domain

@GrailsCompileStatic
@EqualsAndHashCode(includes='email')
@ToString(includes='email', includeNames=true, includePackage=false)
class User implements Serializable {

    private static final long serialVersionUID = 1

    String username
    String password
    boolean enabled = true
    boolean accountExpired
    boolean accountLocked
    boolean passwordExpired
    String firstName
    String lastName

    Set<Role> getAuthorities() {
        (UserRole.findAllByUser(this) as List<UserRole>)*.role as Set<Role>
    }

    static constraints = {
        password nullable: false, blank: false, password: true
        username nullable: false, blank: false, unique: true
    }

    static mapping = {
        password column: '`password`'
    }
}

UserRole - domain

@GrailsCompileStatic
@ToString(cache=true, includeNames=true, includePackage=false)
class UserRole implements Serializable {

    private static final long serialVersionUID = 1

    User user
    Role role

    @Override
    boolean equals(other) {
        if (other instanceof UserRole) {
            other.userId == user?.id && other.roleId == role?.id
        }
    }

    @Override
    int hashCode() {
        int hashCode = HashCodeHelper.initHash()
        if (user) {
            hashCode = HashCodeHelper.updateHash(hashCode, user.id)
        }
        if (role) {
            hashCode = HashCodeHelper.updateHash(hashCode, role.id)
        }
        hashCode
    }

    static UserRole get(long userId, long roleId) {
        criteriaFor(userId, roleId).get()
    }

    static boolean exists(long userId, long roleId) {
        criteriaFor(userId, roleId).count()
    }

    private static DetachedCriteria criteriaFor(long userId, long roleId) {
        UserRole.where {
            user == User.load(userId) &&
            role == Role.load(roleId)
        }
    }

    static UserRole create(User user, Role role, boolean flush = false) {
        def instance = new UserRole(user: user, role: role)
        instance.save(flush: flush)
        instance
    }

    static boolean remove(User u, Role r) {
        if (u != null && r != null) {
            UserRole.where { user == u && role == r }.deleteAll()
        }
    }

    static int removeAll(User u) {
        u == null ? 0 : UserRole.where { user == u }.deleteAll() as int
    }

    static int removeAll(Role r) {
        r == null ? 0 : UserRole.where { role == r }.deleteAll() as int
    }

    static constraints = {
        user nullable: false
        role nullable: false, validator: { Role r, UserRole ur ->
            if (ur.user?.id) {
                if (UserRole.exists(ur.user.id, r.id)) {
                    return ['userRole.exists']
                }
            }
        }
    }

    static mapping = {
        id composite: ['user', 'role']
        version false
    }
}
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.748087 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO