在Grails spring-security-core中自动转换密码哈希

我一直在研究有关转换密码哈希的Stack Overflow问题 ,并意识到使用spring-security-core插件自动执行此过程是可能且相当方便的。

首先,我们需要一个可以同时使用两种算法的PasswordEncoder 。 在这里,我假设您将从SHA-256(可选用盐)转换为bcrypt,但一般方法大多与算法无关。 Sha256ToBCryptPasswordEncoder将始终使用bcrypt哈希新密码,但可以在isPasswordValid检测SHA-256和bcrypt的哈希值之间的区别:

package com.burtbeckwith.grails.security;

import grails.plugin.springsecurity.authentication.encoding.BCryptPasswordEncoder;

import org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder;

public class Sha256ToBCryptPasswordEncoder
       implements org.springframework.security.authentication.encoding.PasswordEncoder {

   protected MessageDigestPasswordEncoder sha256PasswordEncoder;
   protected BCryptPasswordEncoder bcryptPasswordEncoder;

   public String encodePassword(String rawPass, Object salt) {
      return bcryptPasswordEncoder.encodePassword(rawPass, null);
   }

   public boolean isPasswordValid(String encPass,
            String rawPass, Object salt) {
      if (encPass.startsWith("$2a$10$") && encPass.length() == 60) {
         // already bcrypt
         return bcryptPasswordEncoder.isPasswordValid(
                    encPass, rawPass, null);
      }

      if (encPass.length() == 64) {
         return sha256PasswordEncoder.isPasswordValid(
                    encPass, rawPass, salt);
      }

      // TODO
      return false;
   }

   /**
    * Dependency injection for the bcrypt password encoder
    * @param encoder the encoder
    */
   public void setBcryptPasswordEncoder(BCryptPasswordEncoder encoder) {
      bcryptPasswordEncoder = encoder;
   }

   /**
    * Dependency injection for the SHA-256 password encoder
    * @param encoder the encoder
    */
   public void setSha256PasswordEncoder(
           MessageDigestPasswordEncoder encoder) {
      sha256PasswordEncoder = encoder;
   }
}

对于正确配置的SHA-256和bcrypt编码器,这需要依赖注入,稍后我们将看到。

Sha256ToBCryptPasswordEncoder不能做任何修改,因为只有密码信息是可用的,所以我们会继承DaoAuthenticationProvider ,做了这项工作additionalAuthenticationChecks

package com.burtbeckwith.grails.security

import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.userdetails.GrailsUser

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.userdetails.UserDetails

class PasswordFixingDaoAuthenticationProvider
extends DaoAuthenticationProvider {

   def grailsApplication

   protected void additionalAuthenticationChecks(
         UserDetails userDetails,
         UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
      super.additionalAuthenticationChecks userDetails, authentication

      // if we got this far the password was ok

      String oldHashedPassword = userDetails.getPassword()
      if (oldHashedPassword.startsWith('$2a$10$') &&
            oldHashedPassword.length() == 60) {
         // already bcrypt
         return
      }

      if (oldHashedPassword.length() != 64) {
         // TODO
         return
      }

      String bcryptPassword = getPasswordEncoder().encodePassword(
               authentication.credentials, null) 

      // use HQL to update the password in the database directly

      def conf = SpringSecurityUtils.securityConfig
      String userClassName = conf.userLookup.userDomainClassName
      Class<?> User = grailsApplication.getDomainClass(userClassName).clazz

      def args = [p: bcryptPassword]
      String hql = 'update ' + User.name + ' u set u.password=:p where '
      if (userDetails instanceof GrailsUser) {
         hql += 'u.id=:id'
         args.id = userDetails.id
      }
      else {
         hql += 'u.' + conf.userLookup.usernamePropertyName + '=:un'
         args.un = userDetails.username
      }

      User.withNewSession {
         User.withTransaction {
            User.executeUpdate hql, args
         }
      }
   }
}

调用super.additionalAuthenticationChecks()将确保提供了密码,并将通过Sha256ToBCryptPasswordEncoder使用SHA-256或bcrypt对其进行Sha256ToBCryptPasswordEncoder ,因此,如果没有异常抛出,则可以安全地更新密码。 请注意,更新代码是通用的,可以通过对您的类和属性名称进行硬编码来使其更紧凑。

我们将Sha256ToBCryptPasswordEncoder注册为passwordEncoder Bean,并创建bcryptPasswordEncodersha256PasswordEncoder Bean, sha256PasswordEncoder bean配置有正在使用的SHA-256设置以及将要使用的bcrypt设置(如docs中所述在Config.groovy配置)。 另外,将daoAuthenticationProvider的Bean覆盖配置为PasswordFixingDaoAuthenticationProvider ,其配置与SpringSecurityCoreGrailsPlugin.groovy所做的配置相同,并添加grailsApplication参考:

import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.authentication.encoding.BCryptPasswordEncoder

import org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder

import com.burtbeckwith.grails.security.PasswordFixingDaoAuthenticationProvider
import com.burtbeckwith.grails.security.Sha256ToBCryptPasswordEncoder

beans = {

   def conf = SpringSecurityUtils.securityConfig

   bcryptPasswordEncoder(BCryptPasswordEncoder, conf.password.bcrypt.logrounds) // 10

   sha256PasswordEncoder(MessageDigestPasswordEncoder, conf.password.algorithm) {
      encodeHashAsBase64 = conf.password.encodeHashAsBase64 // false
      iterations = conf.password.hash.iterations // 10000
   }

   passwordEncoder(Sha256ToBCryptPasswordEncoder) {
      bcryptPasswordEncoder = ref('bcryptPasswordEncoder')
      sha256PasswordEncoder = ref('sha256PasswordEncoder')
   }

   daoAuthenticationProvider(PasswordFixingDaoAuthenticationProvider) {
      userDetailsService = ref('userDetailsService')
      passwordEncoder = ref('passwordEncoder')
      userCache = ref('userCache')
      saltSource = ref('saltSource')
      preAuthenticationChecks = ref('preAuthenticationChecks')
      postAuthenticationChecks = ref('postAuthenticationChecks')
      authoritiesMapper = ref('authoritiesMapper')
      hideUserNotFoundExceptions = conf.dao.hideUserNotFoundExceptions // true
      grailsApplication = ref('grailsApplication')
   }
}

使用此配置,新用户的密码将使用bcrypt进行哈希处理,有效的现有用户密码将使用登录期间使用的明文密码转换为bcrypt。 转换用户后,请撤消这些更改并转换为标准bcrypt方法。 这将涉及删除grails.plugin.springsecurity.password.algorithm属性和所有盐配置,因为bcrypt不支持盐,删除Sha256ToBCryptPasswordEncoderPasswordFixingDaoAuthenticationProvider ,并从resources.groovy删除bcryptPasswordEncodersha256PasswordEncoder bean定义以及passwordEncoderdaoAuthenticationProvider覆盖。因为由插件使用Config.groovy设置配置的bean就足够了。 另外,如果您已经将盐添加到User类的encodePassword方法中,例如

protected void encodePassword() {
   password = springSecurityService.encodePassword(password, username)
}

不用盐即可将其转换回默认值:

protected void encodePassword() {
   password = springSecurityService.encodePassword(password)
}


翻译自: https://www.javacodegeeks.com/2014/01/automatically-converting-password-hashes-in-grails-spring-security-core.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值