【问题标题】:Devise confirmation tokens in Java在 Java 中设计确认令牌
【发布时间】:2013-11-29 15:50:30
【问题描述】:

似乎无法创建一种功能性方式来从 Java for Devise 插入用户。目前有这些字段: “_ID”, “访问权限”, "confirmation_sent_at", “确认令牌”, “已确认”, “电子邮件”, “加密密码”, “登录计数”

我能够插入算作用户的文档。问题是当我去: http://www.mysite.com:3000/users/confirmation?confirmation_token=TOKENHERE

我收到一条消息说它无效。

编辑 1: 当我重新发送此用户(生成新令牌) 的确认说明时,该用户可以登录。这证实了我对令牌是问题的怀疑。如何将 Devise 的令牌生成器移植到 Java?

编辑 2: 当我在网站上注册时,它说我应该检查确认链接。但是,如果我进入 Mongo shell,手动取出确认令牌并将其粘贴到 site.com/users/confirmation?confirmation_token= 然后它不起作用!但是,如果我真的使用发送给我的确认链接,它就可以工作。我怎样才能制作一个有效的令牌,全部来自 Java。请帮忙!

【问题讨论】:

    标签: java ruby-on-rails mongodb devise bcrypt


    【解决方案1】:

    如果其他人发现自己试图让 java 或 scala 应用程序与 rails 应用程序共存,我破解了以下内容。它在 scala 中,但使用 java api,所以应该很容易阅读。据我所知,它复制了 Devise 的行为,如果我使用原始令牌在 rails 应用程序中点击确认链接,rails/devise 会生成相同的编码字符串。

    import java.security.spec.KeySpec
    import javax.crypto.SecretKey
    import javax.crypto.SecretKeyFactory
    import javax.crypto.spec.PBEKeySpec
    import javax.crypto.spec.SecretKeySpec
    import javax.crypto.Mac
    import javax.xml.bind.DatatypeConverter
    import java.util.Base64 
    
    // copy functionality from Rails Devise
    object TokenGenerator {
    
      // sample values 9exithzwZ8P9meqdVs3K => 54364224169895883e87c8412be5874039b470e26e762cb3ddc37c0bdcf014f5
      //              5zNMi6egbyPoDUy2t3NY => 75bd5d53aa36d3fc61ac186b4c6e2be8353e6b39536d3cf846719284e05474ca
    
      private val deviseSecret = sys.env("DEVISE_SECRET")
      private val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
      val encoder = Base64.getUrlEncoder()
    
      case class TokenInfo(raw: String, encoded: String)
    
      def createConfirmationToken: TokenInfo = {
        // copy behavior from rails world. Don't know why it does this
        val replacements = Map('l' -> "s", 'I' -> "x", 'O' -> "y", '0' -> "z")
    
        // make a raw key of 20 chars, doesn't seem to matter what they are, just need url valid set
        val bytes = new Array[Byte](16)
        scala.util.Random.nextBytes(bytes)
        val raw = encoder.encodeToString(bytes).take(20).foldLeft(""){(acc, x) => acc ++ replacements.get(x).getOrElse(x.toString)}
        TokenInfo(raw, digestForConfirmationToken(raw))
      }
    
      private def generateKey(salt: String): Array[Byte] = {
        val iter = 65536
        val keySize = 512
        val spec = new PBEKeySpec(deviseSecret.toCharArray, salt.getBytes("UTF-8"), iter, keySize)
        val sk = factory.generateSecret(spec)
        val skspec = new SecretKeySpec(sk.getEncoded, "AES")
        skspec.getEncoded
      }
    
      def sha256HexDigest(s: String, key: Array[Byte]): String = {
        val mac = Mac.getInstance("HmacSHA256")
        val keySpec = new SecretKeySpec(key, "RAW")
        mac.init(keySpec)
        val result: Array[Byte] = mac.doFinal(s.getBytes())
        DatatypeConverter.printHexBinary(result).toLowerCase
      }
    
      private def getDigest(raw: String, salt: String) = sha256HexDigest(raw, generateKey(salt))
    
      // devise uses salt "Devise #{column}", in this case its confirmation_token
      def digestForConfirmationToken(raw: String) = getDigest(raw, "Devise confirmation_token")
    }
    

    【讨论】:

      【解决方案2】:

      对于这个问题,您应该参考this stackoverflow answerprotect_from_forgery 的Rails API。

      简短的回答是在您的控制器中禁用伪造保护,但这会使您的应用程序容易受到 CSRF 攻击:

      skip_before_action :verify_authenticity_token
      

      更好的方法是使用 JSON 或 XML 请求进行身份验证,因为这些请求不受 CSRF 保护的保护。你可以找到设计here的解决方案。

      编辑

      猴子补丁设计用于保存未编码的确认令牌。在你的 config/initializers/devise.rb

      module Devise
        module Models
          module Confirmable
            def generate_confirmation_token
              raw, enc = Devise.token_generator.generate(self.class, :confirmation_token)
              @raw_confirmation_token = raw
              self.my_unencoded_column = raw # Patch
              self.confirmation_token = enc
              self.confirmation_sent_at = Time.now.utc
            end
          end
        end
      end
      

      【讨论】:

      • 您好,我想您可能误解了我的问题。我对表单身份验证令牌没有问题,我的问题是通过 Java 将用户注册到 Devise,同时还使用可确认模块。我目前可以从 Java 成功注册用户,但无法生成有效的令牌。这是我目前所知道的:当用户注册并向他们的电子邮件发送令牌时,该令牌与存储在用户文档中“confirmation_token”字段中的令牌不同。设计显然有一些制作独特令牌的方法,我需要将它移植到 Java。
      • 你好,请原谅误解。关于这个话题,我可以发现设计通过调用:OpenSSL::HMAC.hexdigest(@digest, key, raw) in src 来生成令牌第 221 行。问题是设计没有将空白键存储在数据库中,而只是编码的一个。也许可以给班级打补丁。
      • 我的设计初始化文件看起来不是那样的。我会把它放在哪里?我是Rails的新手,对不起!另外,如果我需要通过 Java 存储用户怎么办?我真的不能从 Java 调用该方法,这是否允许在 Java 中生成的令牌工作?
      • 好的。如果要从 Java 端插入记录,则需要创建原始密钥并使用 Java 版本的 OpenSSL::HMAC.hexdigest(@digest, key, raw) 对其进行加密,但这在我看来似乎很复杂。抱歉,没有更多的想法。
      • 好的!我终于想我明白必须做什么了。 pastebin.com/B6pDDcbP 我从 Devise 注册了一个用户,并试图通过加密发送到电子邮件的令牌来获取数据库中的令牌。不幸的是,Devise 从列号生成一个键作为盐,然后进一步有办法防止重新执行生成器。知道如何按照他们的方式制作钥匙吗?这里:github.com/plataformatec/devise/blob/…
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多