替换密码与不同的字母长度

我想实现一个简单的替换密码来掩盖URL中的私有ID。

我知道我的ID会是什么样子(大写ASCII字母,数字和下划线的组合),它们会很长,因为它们是组合键。 我想使用更长的字母来缩短生成的代码(我想使用大写和小写的ASCII字母,数字和其他内容)。 所以我输入的字母表就是

[A-Z0-9_] (37 chars) 

而我的外传字母表将是

 [A-Za-z0-9] (62 chars) 

因此可以获得几乎50%合理压缩量的压缩。

假设我的url如下所示:

 /my/page/GFZHFFFZFZTFZTF_24_F34 

而我希望它们看起来像这样:

 /my/page/Ft32zfegZFV5 

显然,两个数组都会被洗牌以带来一些随机顺序。

这不一定是安全的。 如果有人弄清楚:很好,但我不希望这个计划显而易见。

我想要的解决方案是将字符串转换为基数37的整数表示,将基数转换为62并使用第二个字母表来写出该数字。 是否有任何类似的示例代码? Integer.parseInt()具有一些类似的逻辑,但它使用标准数字行为进行硬编码。

有任何想法吗?

我使用Java来实现这一点,但任何其他语言的代码或伪代码当然也很有帮助。

令人费解的是, Character.MAX_RADIX只有36,但您总是可以编写自己的基本转换例程。 以下实现不是高性能,但它应该是一个很好的起点:

 import java.math.BigInteger; public class BaseConvert { static BigInteger fromString(String s, int base, String symbols) { BigInteger num = BigInteger.ZERO; BigInteger biBase = BigInteger.valueOf(base); for (char ch : s.toCharArray()) { num = num.multiply(biBase) .add(BigInteger.valueOf(symbols.indexOf(ch))); } return num; } static String toString(BigInteger num, int base, String symbols) { StringBuilder sb = new StringBuilder(); BigInteger biBase = BigInteger.valueOf(base); while (!num.equals(BigInteger.ZERO)) { sb.append(symbols.charAt(num.mod(biBase).intValue())); num = num.divide(biBase); } return sb.reverse().toString(); } static String span(char from, char to) { StringBuilder sb = new StringBuilder(); for (char ch = from; ch <= to; ch++) { sb.append(ch); } return sb.toString(); } } 

然后你可以有一个main()测试工具,如下所示:

 public static void main(String[] args) { final String SYMBOLS_AZ09_ = span('A','Z') + span('0','9') + "_"; final String SYMBOLS_09AZ = span('0','9') + span('A','Z'); final String SYMBOLS_AZaz09 = span('A','Z') + span('a','z') + span('0','9'); BigInteger n = fromString("GFZHFFFZFZTFZTF_24_F34", 37, SYMBOLS_AZ09_); // let's convert back to base 37 first... System.out.println(toString(n, 37, SYMBOLS_AZ09_)); // prints "GFZHFFFZFZTFZTF_24_F34" // now let's see what it looks like in base 62... System.out.println(toString(n, 62, SYMBOLS_AZaz09)); // prints "ctJvrR5kII1vdHKvjA4" // now let's test with something we're more familiar with... System.out.println(fromString("CAFEBABE", 16, SYMBOLS_09AZ)); // prints "3405691582" n = BigInteger.valueOf(3405691582L); System.out.println(toString(n, 16, SYMBOLS_09AZ)); // prints "CAFEBABE" } 

一些观察

  • 如果数字可以超过long BigInteger可能是最简单的
  • 你可以在符号String洗牌,只需坚持一个“秘密”排列

关于“50%压缩”的注意事项

您通常不能指望基本62字符串大约是基本字符串36的一半。 这是基数10,20和30中的Long.MAX_VALUE

  System.out.format("%s%n%s%n%s%n", Long.toString(Long.MAX_VALUE, 10), // "9223372036854775807" Long.toString(Long.MAX_VALUE, 20), // "5cbfjia3fh26ja7" Long.toString(Long.MAX_VALUE, 30) // "hajppbc1fc207" ); 

它根本不是替代密码,但你的问题很清楚。

看看Base85: http : //en.wikipedia.org/wiki/Ascii85

对于Java(由维基百科文章间接链接):

我现在有一个可以在这里找到的工作解决方案:

http://pastebin.com/Mctnidng

问题是a)通过这部分,我在长代码中失去了精确度:

 value = value.add(// BigInteger.valueOf((long) Math.pow(alphabet.length, i)) // error here .multiply( BigInteger.valueOf(ArrayUtils.indexOf(alphabet, c)))); 

(很长一段时间不够长)

并且b)每当我有一个以字母表中偏移0处的字符开头的文本时,这将被删除,所以我需要添加一个长度字符(单个字符在这里会很好,因为我的代码永远不会那么长作为字母表)