前言

最近在项目中遇到一个问题,当前用户分享一个邀请码给好友,好友根据邀请码注册成为新用户之后,则成为当前用户的下级,特定条件下,可以得到下级用户的一系列返利。这里要实现的就是根据当前用户的id,生成一个加密串,并且可以反向解密。经过不断的测试调整,终于得到了最后的结果。如:

id = 12 code = 85U43DM

初次实现

先上代码,如下:

/**
* 加密解密用户邀请码,
* @param unknown $string
* @param string $action encode|decode
* @return string
*/
function endecodeUserId($string, $action = 'encode') {
$startLen = 13;
$endLen = 8; $coderes = '';
#TOD 暂设定uid字符长度最大到9
if ($action=='encode') {
$uidlen = strlen($string);
$salt = 'yourself_code';
$codestr = $string.$salt;
$encodestr = hash('md4', $codestr);
$coderes = $uidlen.substr($encodestr, 5,$startLen-$uidlen).$string.substr($encodestr, -12,$endLen);
$coderes = strtoupper($coderes);
}elseif($action=='decode'){
$strlen = strlen($string);
$uidlen = $string[0];
$coderes = substr($string, $startLen-$uidlen+1,$uidlen);
}
return $coderes;
}

思路介绍:

设定一个盐值,$salt,和id拼接后组成一个新的字符串,该盐值可用于后期对邀请码进行安全校验。对该字符串进行md4加密(考虑到相比md5,md4速度更快,并且安全性也并不弱),得到$encodestr,对该字符串进行拆分,分为前后两部分,第一部分$startLen,13个字符串;第二部分$endLen,8个字符串。将$string,这里指传入的id,和$uidlen,混入前一部分字符串。因这里目前仅支持id最大长度为9,因此$uidlen长度为1,这样最后我们便得到了一个长度为22的字符串。

加密的过程中,我们实际上是把id的数值和id的长度,混入到了加密串中,加密的时候我们根据存入的这些信息找到对应的位置,即可得到id。

这里,我们对安全性并没有要求很高,为了使程序运行速度更快,因此在解密的时候并没有验证。

测试,对id加密:

echo endecodeUserId(12);

输出结果:

23471DC2352712F34D6780

测试,对邀请码解密

 echo endecodeUserId('23471DC2352712F34D6780','decode');

输出结果:

12

得到的结果看上去并没有问题,但是实际测试中发现这样一个问题,对于普通用户可能会存在这种情况,好友发到他手机微信上一个邀请码,然后他想要用电脑进行注册,但他并不知道该怎么样把邀请码从手机传到电脑上或者嫌麻烦,这时候他就要在电脑开始手动输入邀请码了,天哪,22位啊,还是大写字母加数字混合,估计他要放弃注册了。

因此,我们进行了调整,改成7位的邀请码。

再次探索

这里是在写文章之前对方法进行了封装,还是直接先上代码


<?php class convert
{
/**
* 初始数字,自定义
*/
const INIT_NUM = 123456789; /**
* @var 进制的基本字符串
*/
private $baseChar; /**
* @var 进制类型
*/
private $type; /**
* @var array 各进制字符串列表
*/
private static $convertList = array(
'32' => '0123456789ABCDEFGHJKMNPQRSTVWXYZ',//不含ILOU
); public function __construct($type='32')
{
$this->type = $type;
$this->baseChar = self::$convertList[$type];
} /**
* 公用方法,数字进行进制转换
* @param $num
* @return string
*/
private function _idToString($num){
$str = '';
while ($num!=0){
$tmp = $num % $this->type;
$str .= $this->baseChar[$tmp];
$num = intval($num/$this->type);
} return $str;
} /**
* @desc im:十机制数转换成三十二进制数
* @param (string)$char 三十二进制数
* return 返回:十进制数
*/
public function idToString($id){//10位内id 返回7位字母数字
//数组 增加备用数值
$id += self::INIT_NUM; //左补0 补齐10位
$str = str_pad($id,10,'0',STR_PAD_LEFT); //按位 拆分 4 6位(32进制 4 6位划分)
$num1 = intval($str[0].$str[2].$str[6].$str[9]);
$num2 = intval($str[1].$str[3].$str[4].$str[5].$str[7].$str[8]);
$str1 = $str2 = ''; $str1 = $this->_idToString($num1);
$str1 = strrev($str1); $str2 = $this->_idToString($num2);
$str2 = strrev($str2); //4 补足 3 4位 U L
return str_pad($str1,3,'U',STR_PAD_RIGHT).str_pad($str2,4,'L',STR_PAD_RIGHT);
} /**
* @desc im:三十二进制数转换成十机制数
* @param (string)$char 三十二进制数
* return 返回:十进制数
*/
public function stringToId($str){
//1 清除 3 4 位补足位
$str1 = trim(substr($str,0,3),'U');
$str2 = trim(substr($str,3,4),'L'); $num1 = $this->_stringToId($str1);
$num2 = $this->_stringToId($str2);
//补位拼接
$str1 = str_pad($num1,4,'0',STR_PAD_LEFT);
$str2 = str_pad($num2,6,'0',STR_PAD_LEFT);
$id = ltrim($str1[0].$str2[0].$str1[1].$str2[1].$str2[2].$str2[3].$str1[2].$str2[4].$str2[5].$str1[3],'0');
//减去 备用数值
$id -= self::INIT_NUM;
return $id;
} /**
* 公用方法字符串转数字
* @param $str
* @return float|int|string
*/
private function _stringToId($str){
//转换为数组
$charArr = array_flip(str_split($this->baseChar));
$num = 0;
for ($i=0;$i<=strlen($str)-1;$i++)
{
$linshi = substr($str,$i,1);
if(!isset($charArr[$linshi])){
return '';
}
$num += $charArr[$linshi]*pow($this->type,strlen($str)-$i-1);
} return $num;
}
}

思路介绍

在一位工作多年的大神的指导下,采用了这种方法。将id转化为固定长度的32进制字符串,并加上自己的算法。为什么这里采用32进制,而不是其他进制呢?32进制可以包含足够多的英文字符,生成的加密串看起来会更规范,另一方面,排除一些不容易识别的英文字符(这里排除ILOU),因此采用了32进制,而并没有采用36进制。

加密过程,方法idToString(),因考虑到刚开始id比较小的时候,转为32进制会出现比较多的0,看起来很不规范,因此设定一个初始值INIT_NUM,这个可以自定义。根据传过来的id,加上初始值后得到一个长度为10位的数值,将这个数值间隔位拆开分为长度为4位的$num1和长度为6位的$num2,两个数值分别转换为32进制,$num1转化后得到长度为3的字符串,不足的用U补足,$num2得到长度为4的字符串,不足的用L来补足。

解密则是逆操作,反向操作即可。

测试:生成

$obj = new convert(32);
$res1 = $obj->idToString(12);

结果:

85U43DM

解密:

$obj = new convert(32);
$res1 = $obj->stringToId('85U43DM');

结果:

12

总结

当然,即使最后的这个方法中也存在有不足的地方,比如在对加密数值拆分为2个num值的时候,用的方法就很不灵活,一旦修改解密的地方也要跟着变动。这里只是分享一个思路,欢迎大家批评指正。

原文地址:https://segmentfault.com/a/1190000015864403

php利用32进制实现对id加密解密的更多相关文章

  1. java利用16进制来辨别png格式的图片

    很多人知道利用.png的字符串结尾可以判断前端传入的图片是否为png格式,但是这只是潜意识的判断!那么如何利用png读写的特殊内容来深意识地判断图片格式呢?最近在做东西的时候遇到了点问题,在加载图片的 ...

  2. 16进制 32进制 base64之间的区别

    Base64: 包含大写字母(A-Z),小写字母(a-z),数字(0-9)以及+/; Base32: 而Base32中只有大写字母(A-Z)和数字234567: Base16: 而Base16就是16 ...

  3. 利用spingmvc及servlet实现对url的地址去除后缀,更改后缀为html

    效果图 1.在web.xml中加上如下配置.其实就是利用servlet的目录过滤,这样所有带有news的地址都会被拦截 <!-- restfull风格约定,去除前台超链接访问的后缀 --> ...

  4. 将MD5值压缩成8位32进制生成8位长度的唯一英文数字组合字符串

    function str16to32($a){ for($a = md5( $a, true ), $s = '0123456789ABCDEFGHIJKLMNOPQRSTUV', $d = '', ...

  5. java代码实现对excel加密、解密(设置或去除打开密码)

    使用jxcell组件来完成对excel加密.解密的功能. jxcell.jar[点击下载](此jar没有使用限制,你懂得) 具体代码如下: import java.io.IOException; im ...

  6. 本大神教你用PHP把文本内容转换成16进制数字,进行加密

    <?php $a="杨波"; $b = bin2hex($a); echo $a."<br />"; $c = pack("H*&q ...

  7. JS中的进制转换以及作用

    js的进制转换, 分为2进制,8进制,10进制,16进制之间的相互转换, 我们直接利用 对象.toString()即可实现: //10进制转为16进制 ().toString() // =>&q ...

  8. sql 进制转换,支持93内的进制相互转换

    功能:实现在SQL内进制的互相转换,支持从2 - 93进制内的转换,若需要支持其他字符,可以自定义@ym变量实现扩充 -- ====================================== ...

  9. PHP进制转换[实现2、8、16、36、64进制至10进制相互转换]

    自己写了一个PHP进制转换程序,一个类吧,第一次写这个东东,写这个东东,在处理文本文件时能用得到.   可以实现: 10进制转换2.8.16.36.62进制2.8.16.36.62进制转换10进制 有 ...

随机推荐

  1. tyvj 1013 找啊找啊找GF

    题目大意: 有一个背包,里面的东西需要满足两个条件,不只是体积 求最多能装多少东西,这些东西的东西最小价值 思路: 双重背包 开两个数组,记录装的东西数量和价值 #include<iostrea ...

  2. 列表渲染v-for

    v-for我们用v-for指令根据一组数据的选项列表进行渲染.v-for指令需要以item in items形式的特殊语法,items是源数据数组并且item是数组元素迭代的别名. demo: < ...

  3. emma中文显示乱码问题解决

    在Linux中如果使用mysql的图形客户端,个人感觉Emma还不错.但是emma默认用apt-get 安装的话,emma是不支持中文的,这个需要自己修改一下了配置文件,或者直接修改emma程序源文件 ...

  4. C#面向过程之局部变量、成员变量、变量作用域、可变参数

    局部变量与成员变量:  局部变量:定义在方法里面的变量就叫做局部变量:没有默认的初始值,使用变量之前必须给它赋值成员变量:定义在类下面的变量叫做成员变量:如果是数值类型默认初始值为0 如果是引用类型默 ...

  5. javascript实现页面跳转

    这里指的页面跳转是将浏览器标签页转到新的网址. 只需要使用 window.location.href="url" 就行了 示例 <!DOCTYPE html> < ...

  6. JPA中关联关系(OneToOne、OneToMany、ManyToMany,ManyToOne)映射代码片段

    在使用Hibernate的时候我们常常会在类里边配置各种的关联关系,但是这个并不是很好配置,配置不当会出现各种各样的问题,下面具体来看一下: 首先我们来看User类里边有一个IdentityCard类 ...

  7. Invalid default value for 'create_date' timestamp field

    创建表的语句中有这么一句 `create_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', 1 之后就报了这个错误. That is be ...

  8. nginx+thinkPhp配置虚拟主机和伪静态规则重写

    /usr/local/nginx/conf/nginx.conf 进行配置 server    {        listen 80 default_server;        #listen [: ...

  9. Storm概念学习系列之storm的定时任务

    不多说,直接上干货! 至于为什么,有storm的定时任务.这个很简单.但是,这个在工作中非常重要! 假设有如下的业务场景 这个spoult源源不断地发送数据,boilt呢会进行处理.然后呢,处理后的结 ...

  10. jQuery学习笔记(3)-操作jQuery包装集的函数

    一.前言 在使用jQuery选择器获取到jQuery包装集后,我们就要对这些包装集进行各种操作 二.创建新的元素 1.使用HTMLDOM创建元素 (1)什么是DOM 当网页被加载时,浏览器会创建页面的 ...