I made the following Controller.
I can login and logout, but how can I force the the user to log in first?
I need to start a session on login and terminate it on logout.
class UserController {
def scaffold = User
def login = {}
def authenticate = {
def user = User.findByLoginAndPassword(params.login, params.password)
if(user){
session.user = user
flash.message = "Hello ${user.name}!"
redirect(controller:"entry", action:"list")
} else {
flash.message = "Sorry, ${params.login}. Please try again."
redirect(action:"login")
}
}
def logout = {
flash.message = "Goodbye ${session.user.name}"
session.user = null
redirect(controller:"entry", action:"list")
}
}
Choice 1:
There are number of security plugins available for securing your Grail's app.
The most popular one is the "Spring Security Core Plugin" which will force user to login before accessing your secured resource.
Reference Link : http://grails.org/plugin/spring-security-core
Choice 2 :
But if you don't want to use any external plugin for you application(I would suggest to use one) you can take advantage of "Filters" in Grail's
You can create a filter for checking the session before user hits any action of your controller and if session is already expired/not created then you can redirect them to login page.
Example :
class SecurityFilterFilters {
def filters = {
loginCheck(controller: 'Your_controller_name(if many separate them with pipe(|) )', action: "*") {
before = {
//check if user is logged in(if yes then there will be session.user) and action is not login action
if (!session.user && !actionName.equals('login')) {
//user is not logged in so redirect him to login page
redirect(controller: 'user', action: 'login')
return false
}
}
}
}
}
Reference Link : http://docs.grails.org/2.2.1/ref/Plug-ins/filters.html
See the 'Secured annotation' spring-security-core plugin documentation for Grails
https://grails-plugins.github.io/grails-spring-security-core/v3/
Related
When I used #RequiresPermissions I got cannot resolve symbol 'RequiresPermissions' error. I already have imported org.apache.shiro.authz.annotation.RequiresPermissions.
Annotations are used as #RequiresPermissions("module:books:list")
My authorization class
class AuthController {
def shiroSecurityManager
def index = { redirect(action: "login", params: params) }
def login = {
return [ username: params.username, rememberMe: (params.rememberMe != null), targetUri: params.targetUri ]
}
def signIn = {
def authToken = new UsernamePasswordToken(params.username, params.password as String)
// Support for "remember me"
if (params.rememberMe) {
authToken.rememberMe = true
}
// If a controller redirected to this page, redirect back
// to it. Otherwise redirect to the root URI.
def targetUri = params.targetUri ?: "/"
// Handle requests saved by Shiro filters.
SavedRequest savedRequest = WebUtils.getSavedRequest(request)
if (savedRequest) {
targetUri = savedRequest.requestURI - request.contextPath
if (savedRequest.queryString) targetUri = targetUri + '?' + savedRequest.queryString
}
try{
// Perform the actual login. An AuthenticationException
// will be thrown if the username is unrecognised or the
// password is incorrect.
SecurityUtils.subject.login(authToken)
log.info "Redirecting to '${targetUri}'."
redirect(uri: targetUri)
}
catch (AuthenticationException ex){
// Authentication failed, so display the appropriate message
// on the login page.
log.info "Authentication failure for user '${params.username}'."
flash.message = message(code: "login.failed")
// Keep the username and "remember me" setting so that the
// user doesn't have to enter them again.
def m = [ username: params.username ]
if (params.rememberMe) {
m["rememberMe"] = true
}
// Remember the target URI too.
if (params.targetUri) {
m["targetUri"] = params.targetUri
}
// Now redirect back to the login page.
redirect(action: "login", params: m)
}
}
def signOut = {
// Log the user out of the application.
SecurityUtils.subject?.logout()
webRequest.getCurrentRequest().session = null
// For now, redirect back to the home page.
redirect(uri: "/")
}
def unauthorized = {
render "You do not have permission to access this page."
}
}
My application runs but when I login by using user that I have set, it directly sends me to unauthorized page. I have allowed some permissions to the user.
If you look at the Javadoc for that annotation (or the source) you'll see that it's allowed to be assigned to types (class-level) and methods (#Target(value={TYPE,METHOD})). You are defining your controller actions as closures, which is still supported in Grails 2.0+ but methods are preferred now. You cannot use that annotation on closures, since they are not methods even though Grails and Groovy let you use them like methods. If the Target annotation included FIELD in addition to the other types it would work, but it doesn't because the Shiro library doesn't have direct support for closures in Grails controllers.
So change all of your actions from closures to methods, e.g.
def index() { redirect(action: "login", params: params) }
def login() {
...
}
....
and then you can annotate them.
I have the following grails controller
class UserController {
def userService
def roleService
def index() {
def roles = roleService.listRoles()
[roles: roles]
}
def userDetails() {
[user: userService.getUser(params.id), role:params.role]
}
def updateUser() {
def user = userService.getUser(params.id)
if (!(params.username)) {
flash.message = "You have to enter a username!"
redirect(action: "userDetails")
}
else {
user.username = params.username
user.person.title = params.title
user.person.given = params.given
user.person.middle = params.middle
user.person.family = params.family
userService.updateUser(user)
redirect(action: "index")
}
}
}
Starting with index() the user gets a list of all roles and users currently available. The user may then select one particular user being linked to the userDetails()-action. There I retrieve the information about the id of the user with params.id and the user's role name with params.role.
In userDetails.gsp the user is able to update some of the user's properties. However, if he doesn't enter a username, he should be redirected back to the userDetails.gsp. (I know that I could check this with the required-attribute within the gsp - it's just to understand the functionality)
And here is where I get stuck - when using the userDetails()-action, two parameters are passed to the gsp. But now when committing the redirect I don't know how to access this information. As a result, rendering the userDetails.gsp results in an error as the required information concerning the user and the role are not available.
Any help would be highly appreciated!
You should change the form (presumably) that submits to the updateUser action, so that it also sends in the role. Then if the data submitted is invalid, you simply include these parameters when redirecting back to the userDetails action.
def updateUser() {
def user = userService.getUser(params.id)
// I'm not sure if this the right way to get a Role from the role parameter
// but presumably you can figure that out yourself
def role = roleService.getRole(params.role)
if (!(params.username)) {
flash.message = "You have to enter a username!"
redirect action: "userDetails", params: [id: params.id, role: params.role]
}
}
As an aside, the way you're manually binding each parameter to the user object is unnecessarily verbose. Grails' databinding can do this automatically.
My Grails app uses the Spring Security plugin. Whenever a user successfully logs in I want to:
store something in the session
redirect them to a custom page (depending on their role)
I need to handle logout events similarly, which was pretty straightforward because the plugin provides a bean named logoutSuccessHandler that can be overriden. I was hoping to similarly find a bean named loginSuccessHandler, but no such luck.
I read the page in the plugin's docs about event handling, but neither of the event handling mechanisms appears to give me access to the current request or session.
If you want to do some stuff upon successful login. You can listen to InteractiveAuthenticationSuccessEvent
class AuthenticationSuccessEventListener implements
ApplicationListener<InteractiveAuthenticationSuccessEvent> {
#Override
public void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
.......do some stuff here
}
}
And then register AuthenticationSuccessEventListener as a spring bean in resources.groovy
You can do whatever you want here, however you wont be able to do redirect from listener.
Here's another similar question
Add a config parameter:
grails.plugins.springsecurity.successHandler.defaultTargetUrl = '/myLogin/handleSuccessLogin'
Then add your custom login-handling in the action that handles this URL
class MyLoginController {
def springSecurityService
def handleSuccessLogin() {
session.foo = 'bar'
if (springSecurityService.currentUser.username == 'bob') {
redirect action: 'bobLogin'
} else {
redirect action: 'defaultLogin'
}
}
def bobLogin() {
// bob's login handler
}
def defaultLogin() {
// default login handler
}
}
I recently used this in a project for logging in. Its kind of a hack but works for me. I'm using version 1.2.7.3 of the plugin.
def auth() {
def config = SpringSecurityUtils.securityConfig
if (springSecurityService.isLoggedIn()) {
def user = User.get(principal.id)
def roles = user.getAuthorities()
def admin_role = Role.findByAuthority("ROLE_ADMIN")
//this user is not admin
if(!roles.contains(admin_role)){
//perform redirect to appropriate page
}
redirect uri: config.successHandler.defaultTargetUrl
//log.info(getPrincipal().username + "logged in at :"+new Date())
return
}
String view = 'auth'
String postUrl = "${request.contextPath}${config.apf.filterProcessesUrl}"
render view: view, model: [postUrl: postUrl,
rememberMeParameter: config.rememberMe.parameter]
}
For logging out I used a Logout controller, performed some action before redirecting to the logout handler:
class LogoutController {
/**
* Index action. Redirects to the Spring security logout uri.
*/
def index = {
// perform some action here
redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl
}
}
I have a Grails application with Spring Security implemented (CAS). However, after I hit the logout button, I am not logged out. I have the LogoutController, with following code:
class LogoutController {
def index = {
println "IN LOGOUT CONTROLLER TRYING TO LOGOUT"
redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/j_spring_security_logout'
}
def accessDenied = {
}
}
And in my Config.groovy file, I have:
grails.plugins.springsecurity.logout.afterLogoutUrl = 'https://login.test.com/cas/logout'
The code for my GSP page is
<g:link controller='Logout'>Logout</g:link>
However, when I click the logout button I get redirected, but not fully logged out. Any ideas on what I am doing wrong?
Have you tried call session.invalidate() direct in the controller index method?
class LogoutController {
def index = {
println "IN LOGOUT CONTROLLER TRYING TO LOGOUT"
session.invalidate()
redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl
}
}
Cheers
<g:link controller='Logout'>Logout</g:link>
def index = {
logoutProcessService.process(params)
redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUr
}
Upon creating new users in my system, I am sending them a temporary password via email and setting an property of changePasswordNextLogin=true. When they come to log in for the first time, I would like to intercept the flow upon a successful login, check for this this value, and if it is true, redirect them to a change password action. Once the password change has been completed, ideally I would like to send them to their intended destination.
I have been pouring through the default settings and am not seeing - or more likely not interpreting properly - any way to make that happen. It seems that almost every time that I try to cobble some solution together in Grails, I find that someone has already made a much more elegant approach to do the same thing. Is there any functionality built in that would allow this?
If not, I would really appreciate any suggestions on the best approach to make it so.
There is some support for this directly with Spring Security and the grails plugin, but you also have to do some work yourself :)
The domain class that was created when you installed grails-spring-security plugin (and ran the S2Quickstart script) has a property on it named 'passwordExpired'. Set this to true when you create your new user domain instance.
Once that user logs in for the first time, the Spring Security core libs will throw an exception which you can catch in your login controller's authfail closure, re-directing them to the change password form (that you need to supply yourself).
Here's an example from one of my apps, a skeleton version of this closure should already be included in your login controller:
/**
* Callback after a failed login.
*/
def authfail = {
def msg = ''
def username =
session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY]
def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
if (exception) {
if (exception instanceof CredentialsExpiredException) {
msg = g.message(code: "springSecurity.errors.login.passwordExpired")
if (!springSecurityService.isAjax(request))
redirect (action:'changePassword') // <-- see below
}
// other failure checks omitted
}
if (springSecurityService.isAjax(request)) {
render([error: msg] as JSON)
}
else {
flash.message = msg
redirect controller: 'login', action:'auth', params: params
}
}
/**
* render the change pwd form
*/
def changePassword = {
[username: session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY] ?: springSecurityService.authentication.name]
}
From your 'changePasssword' view, submit the form back to another controller closure (I call mine 'updatePassword' that checks whatever constraints you want for passwords and either saves the updated password on the domain object or not..
def updatePassword = {
String username = session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY] ?: springSecurityService.authentication.name
if (!username) {
flash.message = 'Sorry, an error has occurred'
redirect controller: 'login', action:'auth'
return
}
String password = params.password
String newPassword = params.password_new
String newPassword2 = params.password_new_2
if (!password || !newPassword || !newPassword2 || newPassword != newPassword2) {
flash.message = 'Please enter your current password and a new password'
render view: 'changePassword', model: [username: username]
return
}
SecUser user = SecUser.findByUsername(username)
if (!passwordEncoder.isPasswordValid(user.password, password, null /*salt*/)) {
flash.message = 'Current password is incorrect'
render view: 'changePassword', model: [username: username]
return
}
if (passwordEncoder.isPasswordValid(user.password, newPassword, null /*salt*/)) {
flash.message = 'Please choose a different password from your current one'
render view: 'changePassword', model: [username: username]
return
}
if (!newPassword.matches(PASSWORD_REGEX)) {
flash.message = 'Password does not meet minimum requirements'
render view: 'changePassword', model: [username: username]
return
}
// success if we reach here!
user.password = springSecurityService.encodePassword(newPassword)
user.passwordExpired = false
user.save()
flash.message = 'Password changed successfully' + (springSecurityService.loggedIn ? '' : ', you can now login')
redirect uri: '/'
}
If you are using Spring Secuirty 3.0 and later, You can refer to the spring security plugin documentation 11.3 Account Locking and Forcing Password Change.
Remember that you should set
grails.plugin.springsecurity.apf.storeLastUsername=true in Config.groovy.