grails spring-security passwords grails-plugin grails-2.0

el inicio de sesión de seguridad de Grails Spring no funciona



spring-security passwords (2)

Creo que está codificando su contraseña dos veces, ya tiene codificado () en beforeInsert en el dominio, creo que no necesita codificarlo de nuevo.

Estoy usando Grails 2.1.0. He instalado el complemento spring-security-core.

Cuando creo un usuario, lo está creando. Pero cuando intento iniciar sesión, muestra:

"Sorry, we were not able to find a user with that username and password."

Y también hay otro hecho: cuando uso la misma contraseña para diferentes usuarios, no guardo la contraseña con un valor codificado similar, como para el usuario 1. He usado la contraseña 123 que se guarda en una base de datos como esta.

d535ce213a0e8e4f9e724af47c46eea409ef401c03617b749da618a82890d743

y para el usuario 2 también utilicé la contraseña 123 y esta vez se guarda así

0849ea79a2c1bca057ded06c3053fb5bc5d7ba52b50982e73e44894d4f3e0aa6

No entiendo. ¿Puede alguien ayudarme en esto?

mi config.groovy >>>

// locations to search for config files that get merged into the main config; // config files can be ConfigSlurper scripts, Java properties files, or classes // in the classpath in ConfigSlurper format // grails.config.locations = [ "classpath:${appName}-config.properties", // "classpath:${appName}-config.groovy", // "file:${userHome}/.grails/${appName}-config.properties", // "file:${userHome}/.grails/${appName}-config.groovy"] // if (System.properties["${appName}.config.location"]) { // grails.config.locations << "file:" + System.properties["${appName}.config.location"] // } grails.project.groupId = appName // change this to alter the default package name and Maven publishing destination grails.mime.file.extensions = true // enables the parsing of file extensions from URLs into the request format grails.mime.use.accept.header = false grails.mime.types = [ all: ''*/*'', atom: ''application/atom+xml'', css: ''text/css'', csv: ''text/csv'', form: ''application/x-www-form-urlencoded'', html: [''text/html'',''application/xhtml+xml''], js: ''text/javascript'', json: [''application/json'', ''text/json''], multipartForm: ''multipart/form-data'', rss: ''application/rss+xml'', text: ''text/plain'', xml: [''text/xml'', ''application/xml''] ] // URL Mapping Cache Max Size, defaults to 5000 //grails.urlmapping.cache.maxsize = 1000 // What URL patterns should be processed by the resources plugin grails.resources.adhoc.patterns = [''/images/*'', ''/css/*'', ''/js/*'', ''/plugins/*''] // The default codec used to encode data with ${} grails.views.default.codec = "none" // none, html, base64 grails.views.gsp.encoding = "UTF-8" grails.converters.encoding = "UTF-8" // enable Sitemesh preprocessing of GSP pages grails.views.gsp.sitemesh.preprocess = true // scaffolding templates configuration grails.scaffolding.templates.domainSuffix = ''Instance'' // Set to false to use the new Grails 1.2 JSONBuilder in the render method grails.json.legacy.builder = false // enabled native2ascii conversion of i18n properties files grails.enable.native2ascii = true // packages to include in Spring bean scanning grails.spring.bean.packages = [] // whether to disable processing of multi part requests grails.web.disable.multipart=false // request parameters to mask when logging exceptions grails.exceptionresolver.params.exclude = [''password''] // configure auto-caching of queries by default (if false you can cache individual queries with ''cache: true'') grails.hibernate.cache.queries = false environments { development { grails.logging.jul.usebridge = true } production { grails.logging.jul.usebridge = false // TODO: grails.serverURL = "http://www.changeme.com" } } // log4j configuration log4j = { // Example of changing the log pattern for the default console appender: // //appenders { // console name:''stdout'', layout:pattern(conversionPattern: ''%c{2} %m%n'') //} error ''org.codehaus.groovy.grails.web.servlet'', // controllers ''org.codehaus.groovy.grails.web.pages'', // GSP ''org.codehaus.groovy.grails.web.sitemesh'', // layouts ''org.codehaus.groovy.grails.web.mapping.filter'', // URL mapping ''org.codehaus.groovy.grails.web.mapping'', // URL mapping ''org.codehaus.groovy.grails.commons'', // core / classloading ''org.codehaus.groovy.grails.plugins'', // plugins ''org.codehaus.groovy.grails.orm.hibernate'', // hibernate integration ''org.springframework'', ''org.hibernate'', ''net.sf.ehcache.hibernate'' } // Added by the Spring Security Core plugin: grails.plugins.springsecurity.userLookup.userDomainClassName = ''common.auth.User'' grails.plugins.springsecurity.userLookup.authorityJoinClassName = ''common.auth.UserAuthority'' grails.plugins.springsecurity.authority.className = ''common.auth.Authority''

mi controlador de inicio de sesión >>>

import grails.converters.JSON import javax.servlet.http.HttpServletResponse import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils import org.springframework.security.authentication.AccountExpiredException import org.springframework.security.authentication.CredentialsExpiredException import org.springframework.security.authentication.DisabledException import org.springframework.security.authentication.LockedException import org.springframework.security.core.context.SecurityContextHolder as SCH import org.springframework.security.web.WebAttributes import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter class LoginController { /** * Dependency injection for the authenticationTrustResolver. */ def authenticationTrustResolver /** * Dependency injection for the springSecurityService. */ def springSecurityService /** * Default action; redirects to ''defaultTargetUrl'' if logged in, /login/auth otherwise. */ def index = { if (springSecurityService.isLoggedIn()) { redirect uri: SpringSecurityUtils.securityConfig.successHandler.defaultTargetUrl } else { redirect action: ''auth'', params: params } } /** * Show the login page. */ def auth = { def config = SpringSecurityUtils.securityConfig if (springSecurityService.isLoggedIn()) { redirect uri: config.successHandler.defaultTargetUrl return } String view = ''auth'' String postUrl = "${request.contextPath}${config.apf.filterProcessesUrl}" render view: view, model: [postUrl: postUrl, rememberMeParameter: config.rememberMe.parameter] } /** * The redirect action for Ajax requests. */ def authAjax = { response.setHeader ''Location'', SpringSecurityUtils.securityConfig.auth.ajaxLoginFormUrl response.sendError HttpServletResponse.SC_UNAUTHORIZED } /** * Show denied page. */ def denied = { if (springSecurityService.isLoggedIn() && authenticationTrustResolver.isRememberMe(SCH.context?.authentication)) { // have cookie but the page is guarded with IS_AUTHENTICATED_FULLY redirect action: ''full'', params: params } } /** * Login page for users with a remember-me cookie but accessing a IS_AUTHENTICATED_FULLY page. */ def full = { def config = SpringSecurityUtils.securityConfig render view: ''auth'', params: params, model: [hasCookie: authenticationTrustResolver.isRememberMe(SCH.context?.authentication), postUrl: "${request.contextPath}${config.apf.filterProcessesUrl}"] } /** * Callback after a failed login. Redirects to the auth page with a warning message. */ def authfail = { def username = session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY] String msg = '''' def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION] if (exception) { if (exception instanceof AccountExpiredException) { msg = g.message(code: "springSecurity.errors.login.expired") } else if (exception instanceof CredentialsExpiredException) { msg = g.message(code: "springSecurity.errors.login.passwordExpired") } else if (exception instanceof DisabledException) { msg = g.message(code: "springSecurity.errors.login.disabled") } else if (exception instanceof LockedException) { msg = g.message(code: "springSecurity.errors.login.locked") } else { msg = g.message(code: "springSecurity.errors.login.fail") } } if (springSecurityService.isAjax(request)) { render([error: msg] as JSON) } else { flash.message = msg redirect action: ''auth'', params: params } } /** * The Ajax success redirect url. */ def ajaxSuccess = { render([success: true, username: springSecurityService.authentication.name] as JSON) } /** * The Ajax denied redirect url. */ def ajaxDenied = { render([error: ''access denied''] as JSON) } }

mi autoridad.groovy >>>

package common.auth class Authority { String authority static mapping = { cache true } static constraints = { authority blank: false, unique: true } }

mi dominio de usuario.groovy donde se guardará mi usuario >>>

package common.auth class User { transient springSecurityService String realname String username String password String designation boolean enabled boolean accountExpired boolean accountLocked boolean passwordExpired static constraints = { username blank: false, unique: true password blank: false } static mapping = { password column: ''`password`'' } Set<Authority> getAuthorities() { UserAuthority.findAllByUser(this).collect { it.authority } as Set } def beforeInsert() { encodePassword() } def beforeUpdate() { if (isDirty(''password'')) { encodePassword() } } protected void encodePassword() { password = springSecurityService.encodePassword(password) } }

mi userauthority.groovy >>>

package common.auth import org.apache.commons.lang.builder.HashCodeBuilder class UserAuthority implements Serializable { User user Authority authority boolean equals(other) { if (!(other instanceof UserAuthority)) { return false } other.user?.id == user?.id && other.authority?.id == authority?.id } int hashCode() { def builder = new HashCodeBuilder() if (user) builder.append(user.id) if (authority) builder.append(authority.id) builder.toHashCode() } static UserAuthority get(long userId, long authorityId) { find ''from UserAuthority where user.id=:userId and authority.id=:authorityId'', [userId: userId, authorityId: authorityId] } static UserAuthority create(User user, Authority authority, boolean flush = false) { new UserAuthority(user: user, authority: authority).save(flush: flush, insert: true) } static boolean remove(User user, Authority authority, boolean flush = false) { UserAuthority instance = UserAuthority.findByUserAndAuthority(user, authority) if (!instance) { return false } instance.delete(flush: flush) true } static void removeAll(User user) { executeUpdate ''DELETE FROM UserAuthority WHERE user=:user'', [user: user] } static void removeAll(Authority authority) { executeUpdate ''DELETE FROM UserAuthority WHERE authority=:authority'', [authority: authority] } static mapping = { id composite: [''authority'', ''user''] version false } }

Y mi acción createUser para crear usuario en AdministratorActionController >>>

package administrator import common.auth.User class AdmistratorActionController { def springSecurityService def index() { redirect(controller: ''admistratorAction'', action: ''createUser'') } def createUser = { User user = new User(params) def password = user.password def salt = user.username //depends on what you''re using as a salt user.password = springSecurityService.encodePassword(password, salt) user.save() flash.message = "User Create Successfully !!!" } }


Spring Security usa el hash de la contraseña del usuario + una sal. La sal se usa para derrotar los ataques precomputados de la tabla del arco iris que, de lo contrario, se podrían usar para mejorar en gran medida la eficacia de descifrar la base de datos de contraseñas codificadas. Ver http://en.wikipedia.org/wiki/Salt_(cryptography)

Por ejemplo, si usamos un username como hash, el valor real en la base de datos será:

md5(user.password + ''|'' + user.username)

Entonces para dos usuarios diferentes con la misma contraseña:

  • usuario: nombre de usuario: ''usuario1'', contraseña: 123
  • usuario: nombre de usuario: ''usuario2'', contraseña: 123

obtendrás dos valores diferentes en la base de datos:

  • md5(''user1|123'') == 975204d0650cc642730866d56f66b6fb
  • md5(''user2|123'') == aa12022115555842a7f80564940ae49d

Entonces, si el hacker tiene acceso a su base de datos, no puede adivinar la contraseña.

Spring Security usa la misma función de hash y sal para guardar y cargar usuarios. Y si no puede encontrar al usuario, probablemente signifique que ha usado sal diferente para el guardado inicial y para la carga posterior de la base de datos. Asegúrate de tener la misma fuente de sal y no está cambiando (como el campo de id , que tiene valor null cuando creas un nuevo usuario)