如何将整数编码为可作为URL参数值的可逆字符串(Java/JSF)
解决方案:把企业ID转成可逆的URL安全短字符串
针对你的需求——把整数企业ID转成能还原、URL可用、长度适中的字符串,避免被轻易枚举所有企业,给你几个实用的方案,都是Java/JSF环境下能直接用的:
一、自定义进制转换(最省事的方案)
核心想法就是把整数ID转换成自定义的高进制(比如64进制),用URL安全的字符当进制符号。这种方法完全可逆,生成的字符串还特别短。
代码示例(Java)
- 先定义一套URL安全的字符集,比如包含大小写字母、数字、
-和_(这俩在URL里不用编码):
private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
- 写ID转字符串的方法:
public static String encode(long id) { if (id == 0) { return String.valueOf(ALPHABET.charAt(0)); } StringBuilder sb = new StringBuilder(); while (id > 0) { sb.append(ALPHABET.charAt((int) (id % ALPHABET.length()))); id = id / ALPHABET.length(); } return sb.reverse().toString(); }
- 再写字符串转回ID的方法:
public static long decode(String code) { long id = 0; for (char c : code.toCharArray()) { id = id * ALPHABET.length() + ALPHABET.indexOf(c); } return id; }
额外优化:加盐防枚举
要是怕有人瞎猜字符串试所有企业ID,可以给ID加个盐再转换——比如转之前先和一个固定的长整数异或:
// 编码时 long saltedId = originalId ^ 123456789L; String code = encode(saltedId); // 解码时 long saltedId = decode(code); long originalId = saltedId ^ 123456789L;
这样就算别人解码出数值,也不是真实的企业ID,得知道盐才能拿到正确值,大大增加枚举难度。
二、加密式可逆转换(安全性更高)
如果需要更强的安全性,不想让任何人能反向推导出ID,就用对称加密(比如AES),把加密后的字节转成URL安全的Base64格式。
代码示例(Java)
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public class IdEncryptor { // 用16字节的密钥(AES-128),自己换个安全的密钥存好 private static final byte[] AES_KEY = "YourSecureKey123".getBytes(); private static final String ALGORITHM = "AES"; public static String encrypt(long companyId) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte[] encryptedBytes = cipher.doFinal(String.valueOf(companyId).getBytes()); // 用URL安全的Base64,去掉多余的=符号 return Base64.getUrlEncoder().withoutPadding().encodeToString(encryptedBytes); } public static long decrypt(String encryptedCode) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, keySpec); byte[] decodedBytes = Base64.getUrlDecoder().decode(encryptedCode); byte[] decryptedBytes = cipher.doFinal(decodedBytes); return Long.parseLong(new String(decryptedBytes)); } }
注意点
- 密钥一定要妥善保管,丢了的话所有加密后的字符串都没法还原了
- 加密后的字符串长度会比进制转换的长一些,比如ID=5可能转成16位左右的字符串
三、现成工具库(不想自己写代码的话)
- Apache Commons Codec:提供现成的URL安全Base64编码,你可以配合自己的加密/进制转换逻辑用
- ShortUUID:虽然主要用来生成UUID,但也支持自定义字符集和可逆转换,不过对于整数ID来说,自定义进制还是更简单直接
四、在JSF里怎么用
把上面的工具类做成CDI Bean,就能在JSF的Managed Bean里直接调用了:
import javax.enterprise.context.ApplicationScoped; import javax.inject.Named; @Named @ApplicationScoped public class CompanyIdConverter { private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; private static final long SALT = 987654321L; public String encodeCompanyId(long id) { long saltedId = id ^ SALT; return encode(saltedId); } public long decodeCompanyId(String code) { long saltedId = decode(code); return saltedId ^ SALT; } // 这里放上面的encode和decode私有方法 private String encode(long id) { // 实现代码 } private long decode(String code) { // 实现代码 } }
然后在处理登录请求的Bean里获取参数并转换:
import javax.inject.Inject; import javax.faces.bean.ManagedBean; @ManagedBean public class LoginBean { @Inject private CompanyIdConverter companyIdConverter; public void initLogin() { String ociParam = FacesContext.getCurrentInstance().getExternalContext() .getRequestParameterMap().get("OCI"); if (ociParam != null) { long companyId = companyIdConverter.decodeCompanyId(ociParam); // 拿到真实企业ID后,做后续的品牌加载、登录逻辑 } } }
内容的提问来源于stack exchange,提问作者Kevin Rahe
相关产品推荐
相关产品推荐

