Excel中快速获取两列数据中的重复项
如图,需查询A列同E列中的重复项。
在需展示的列单元格中输入:=IF(COUNTIF(E:E,A1),A1,""),意思是用A1单元格的数值在E列中查重,如果有重复值就在单元格中显示重复的数值。
按住ctrl向下填充即可获得两列中的重复项,再进行排序等操作即可。
如图,需查询A列同E列中的重复项。
在需展示的列单元格中输入:=IF(COUNTIF(E:E,A1),A1,""),意思是用A1单元格的数值在E列中查重,如果有重复值就在单元格中显示重复的数值。
按住ctrl向下填充即可获得两列中的重复项,再进行排序等操作即可。
支持阳历阴历相互转换、获取生肖、时辰、节气等老黄历数据。(此接口初始代码来源网络,有待测试)
注:不支持闰年,如2023年
<?php
/**
* Desc: 阳历/农历 转换
* User: SenSen Wechat:1050575278
* Date: 2021/9/4
* Time: 10:42
*/
namespace sensen\services;
class LunarService
{
static public $minYear = 1891;
static public $maxYear = 2100;
static public $lunarInfo = [
[0,2,9,21936],[6,1,30,9656],[0,2,17,9584],[0,2,6,21168],[5,1,26,43344],[0,2,13,59728],[0,2,2,27296],[3,1,22,44368],[0,2,10,43856],[8,1,30,19304],[0,2,19,19168],[0,2,8,42352],[5,1,29,21096],[0,2,16,53856],[0,2,4,55632],[4,1,25,27304],[0,2,13,22176],[0,2,2,39632],[2,1,22,19176],[0,2,10,19168],[6,1,30,42200],[0,2,18,42192],[0,2,6,53840],[5,1,26,54568],[0,2,14,46400],[0,2,3,54944],[2,1,23,38608],[0,2,11,38320],[7,2,1,18872],[0,2,20,18800],[0,2,8,42160],[5,1,28,45656],[0,2,16,27216],[0,2,5,27968],[4,1,24,44456],[0,2,13,11104],[0,2,2,38256],[2,1,23,18808],[0,2,10,18800],[6,1,30,25776],[0,2,17,54432],[0,2,6,59984],[5,1,26,27976],[0,2,14,23248],[0,2,4,11104],[3,1,24,37744],[0,2,11,37600],[7,1,31,51560],[0,2,19,51536],[0,2,8,54432],[6,1,27,55888],[0,2,15,46416],[0,2,5,22176],[4,1,25,43736],[0,2,13,9680],[0,2,2,37584],[2,1,22,51544],[0,2,10,43344],[7,1,29,46248],[0,2,17,27808],[0,2,6,46416],[5,1,27,21928],[0,2,14,19872],[0,2,3,42416],[3,1,24,21176],[0,2,12,21168],[8,1,31,43344],[0,2,18,59728],[0,2,8,27296],[6,1,28,44368],[0,2,15,43856],[0,2,5,19296],[4,1,25,42352],[0,2,13,42352],[0,2,2,21088],[3,1,21,59696],[0,2,9,55632],[7,1,30,23208],[0,2,17,22176],[0,2,6,38608],[5,1,27,19176],[0,2,15,19152],[0,2,3,42192],[4,1,23,53864],[0,2,11,53840],[8,1,31,54568],[0,2,18,46400],[0,2,7,46752],[6,1,28,38608],[0,2,16,38320],[0,2,5,18864],[4,1,25,42168],[0,2,13,42160],[10,2,2,45656],[0,2,20,27216],[0,2,9,27968],[6,1,29,44448],[0,2,17,43872],[0,2,6,38256],[5,1,27,18808],[0,2,15,18800],[0,2,4,25776],[3,1,23,27216],[0,2,10,59984],[8,1,31,27432],[0,2,19,23232],[0,2,7,43872],[5,1,28,37736],[0,2,16,37600],[0,2,5,51552],[4,1,24,54440],[0,2,12,54432],[0,2,1,55888],[2,1,22,23208],[0,2,9,22176],[7,1,29,43736],[0,2,18,9680],[0,2,7,37584],[5,1,26,51544],[0,2,14,43344],[0,2,3,46240],[4,1,23,46416],[0,2,10,44368],[9,1,31,21928],[0,2,19,19360],[0,2,8,42416],[6,1,28,21176],[0,2,16,21168],[0,2,5,43312],[4,1,25,29864],[0,2,12,27296],[0,2,1,44368],[2,1,22,19880],[0,2,10,19296],[6,1,29,42352],[0,2,17,42208],[0,2,6,53856],[5,1,26,59696],[0,2,13,54576],[0,2,3,23200],[3,1,23,27472],[0,2,11,38608],[11,1,31,19176],[0,2,19,19152],[0,2,8,42192],[6,1,28,53848],[0,2,15,53840],[0,2,4,54560],[5,1,24,55968],[0,2,12,46496],[0,2,1,22224],[2,1,22,19160],[0,2,10,18864],[7,1,30,42168],[0,2,17,42160],[0,2,6,43600],[5,1,26,46376],[0,2,14,27936],[0,2,2,44448],[3,1,23,21936],[0,2,11,37744],[8,2,1,18808],[0,2,19,18800],[0,2,8,25776],[6,1,28,27216],[0,2,15,59984],[0,2,4,27424],[4,1,24,43872],[0,2,12,43744],[0,2,2,37600],[3,1,21,51568],[0,2,9,51552],[7,1,29,54440],[0,2,17,54432],[0,2,5,55888],[5,1,26,23208],[0,2,14,22176],[0,2,3,42704],[4,1,23,21224],[0,2,11,21200],[8,1,31,43352],[0,2,19,43344],[0,2,7,46240],[6,1,27,46416],[0,2,15,44368],[0,2,5,21920],[4,1,24,42448],[0,2,12,42416],[0,2,2,21168],[3,1,22,43320],[0,2,9,26928],[7,1,29,29336],[0,2,17,27296],[0,2,6,44368],[5,1,26,19880],[0,2,14,19296],[0,2,3,42352],[4,1,24,21104],[0,2,10,53856],[8,1,30,59696],[0,2,18,54560],[0,2,7,55968],[6,1,27,27472],[0,2,15,22224],[0,2,5,19168],[4,1,25,42216],[0,2,12,42192],[0,2,1,53584],[2,1,21,55592],[0,2,9,54560]
];
/**
* 阳历转阴历
*/
public static function convertSolarToLunar($date='')
{
$date = $date?date('Y-n-j', strtotime($date)):date('Y-n-j');
$dateArr = explode('-', $date);
$yearIndex = $dateArr[0] - self::$minYear;
$yearData = self::$lunarInfo[$yearIndex];
if($dateArr[0] == self::$minYear && $dateArr[1]<=2 && $dateArr[2]<=9){
$return = [1891, '正月', '初一', '辛卯', 1, 1, '兔'];
}else{
$return = self::getLunarByBetween($dateArr[0], self::getDaysBetweenSolar($dateArr[0], $dateArr[1], $dateArr[2], $yearData[1], $yearData[2]));
}
$date = date('Y-m-d', strtotime($return[0].'-'.$return[4].'-'.$return[5]));
return ['date'=>$date, 'info'=>$return];
}
/**
* 获取当前阳历对应的一个月阴历数据
* @param string $date
* @return array
*/
public static function convertSolarMonthToLunar($date='')
{
$date = $date?date('Y-n-j', strtotime($date)):date('Y-n-j');
$dateArr = explode('-', $date);
$yearIndex = $dateArr[0] - self::$minYear;
$yearData = self::$lunarInfo[$yearIndex];
if($dateArr[0]==self::$minYear&&$dateArr[1]<=2&&$dateArr[2]<=9) return array(1891,'正月','初一','辛卯',1,1,'兔');
$month_days_ary = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
$dd = $month_days_ary[$dateArr[1]];
if(self::isLeapYear($dateArr[0]) && $dateArr[1] == 2) $dd++;
$lunar_ary = [];
for ($i = 1; $i < $dd; $i++)
{
$array = self::getLunarByBetween($dateArr[0],self::getDaysBetweenSolar($dateArr[0], $dateArr[1], $i, $yearData[1], $yearData[2]));
$array[] = $dateArr[0] . '-' . $dateArr[1] . '-' . $i;
$lunar_ary[$i] = $array;
}
return $lunar_ary;
}
/**
* 将阴历转换为阳历
* @param year 阴历-年
* @param month 阴历-月,闰月处理:例如如果当年闰五月,那么第二个五月就传六月,相当于阴历有13个月,只是有的时候第13个月的天数为0
* @param date 阴历-日
*/
public static function convertLunarToSolar($date='')
{
$date = $date?date('Y-n-j', strtotime($date)):date('Y-n-j');
$dateArr = explode('-', $date);
$yearIndex = $dateArr[0] - self::$minYear;
$yearData = self::$lunarInfo[$yearIndex];
$between = self::getDaysBetweenLunar($dateArr[0],$dateArr[1],$dateArr[2]);
$res = mktime(0,0,0,$yearData[1],$yearData[2],$dateArr[0]);
$res = date('Y-m-d', $res+$between*24*60*60);
$day = explode('-', $res);
$year = $day[0];
$month= $day[1];
$day = $day[2];
return $year.'-'.$month.'-'.$day;
}
/**
* 判断是否是闰年
* @param year
*/
public static function isLeapYear($year='')
{
$year = $year?:date('Y');
return (($year%4==0 && $year%100 !=0) || ($year%400==0));
}
/**
* 获取干支纪年
* @param year
*/
public static function getLunarYearName($year='')
{
$year = $year?:date('Y');
$sky = array('庚','辛','壬','癸','甲','乙','丙','丁','戊','己');
$earth = array('申','酉','戌','亥','子','丑','寅','卯','辰','巳','午','未');
$year = $year.'';
return $sky[$year[3]].$earth[$year%12];
}
/**
* 根据阴历年获取生肖
* @param year 阴历年
*/
public static function getYearZodiac($year='')
{
$year = $year?:date('Y');
$zodiac = array('猴','鸡','狗','猪','鼠','牛','虎','兔','龙','蛇','马','羊');
return $zodiac[$year%12];
}
/**
* 获取阳历月份的天数
* @param year 阳历-年
* @param month 阳历-月
*/
public static function getSolarMonthDays($year='', $month='')
{
$year = $year?:date('Y');
$month = $month?:date('n');
$monthHash = array('1'=>31,'2'=>self::isLeapYear($year)?29:28,'3'=>31,'4'=>30,'5'=>31,'6'=>30,'7'=>31,'8'=>31,'9'=>30,'10'=>31,'11'=>30,'12'=>31);
return $monthHash["$month"];
}
/**
* 获取阴历月份的天数
* @param year 阴历-年
* @param month 阴历-月,从一月开始
*/
public static function getLunarMonthDays($year='', $month='')
{
$year = $year?:date('Y');
$month = $month?:date('n');
$monthData = self::getLunarMonths($year);
return $monthData[$month-1];
}
/**
* 获取阴历每月的天数的数组
* @param year
*/
public static function getLunarMonths($year='')
{
$year = $year?:date('Y');
$yearData = self::$lunarInfo[$year - self::$minYear];
$leapMonth = $yearData[0];
$bit = decbin($yearData[3]);
for ($i = 0; $i < strlen($bit);$i ++) $bitArray[$i] = substr($bit, $i, 1);
for($k=0,$klen=16-count($bitArray);$k<$klen;$k++) array_unshift($bitArray, '0');
$bitArray = array_slice($bitArray,0,($leapMonth==0?12:13));
for($i=0; $i<count($bitArray); $i++) $bitArray[$i] = $bitArray[$i] + 29;
return $bitArray;
}
/**
* 获取农历每年的天数
* @param year 农历年份
*/
public static function getLunarYearDays($year='')
{
$year = $year?:date('Y');
$yearData = self::$lunarInfo[$year-self::$minYear];
$monthArray = self::getLunarYearMonths($year);
$len = count($monthArray);
return ($monthArray[$len-1]==0?$monthArray[$len-2]:$monthArray[$len-1]);
}
/**
* 获取阴历月份对应的总天数
* @param string $year
* @return array
*/
public static function getLunarYearMonths($year='')
{
$year = $year?:date('Y');
$monthData = self::getLunarMonths($year);
$res = [];
$yearData = self::$lunarInfo[$year-self::$minYear];
$len = ($yearData[0]==0?12:13);
for($i=0;$i<$len;$i++)
{
$temp=0;
for($j=0;$j<=$i;$j++) $temp+=$monthData[$j];
array_push($res, $temp);
}
return $res;
}
/**
* 获取闰月
* @param year 阴历年份
*/
public static function getLeapMonth($year='')
{
$year = $year?:date('Y');
$yearData = self::$lunarInfo[$year-self::$minYear];
return $yearData[0];
}
/**
* 计算阴历日期与正月初一相隔的天数
* @param date
*/
public static function getDaysBetweenLunar($date='')
{
$date = $date?date('Y-n-j', strtotime($date)):date('Y-n-j');
$dateArr = explode('-', $date);
$yearMonth = self::getLunarMonths($dateArr[0]);
$res=0;
for($i=1;$i<$dateArr[1];$i++) $res +=$yearMonth[$i-1];
$res+=$dateArr[2]-1;
return $res;
}
/**
* 计算2个阳历日期之间的天数
* @param year 阳历年
* @param cmonth
* @param cdate
* @param dmonth 阴历正月对应的阳历月份
* @param ddate 阴历初一对应的阳历天数
*/
public static function getDaysBetweenSolar($year,$cmonth,$cdate,$dmonth,$ddate)
{
$a = mktime(0,0,0,$cmonth,$cdate,$year);
$b = mktime(0,0,0,$dmonth,$ddate,$year);
return ceil(($a-$b)/24/3600);
}
/**
* 根据距离正月初一的天数计算阴历日期
* @param year 阳历年
* @param between 天数
*/
public static function getLunarByBetween($year='', $between=0)
{
$year = $year?:date('Y');
$lunarArray = [];
$yearMonth=[];
$t=0;
$e=0;
$leapMonth=0;
$m='';
if($between==0)
{
array_push($lunarArray, $year,'正月','初一');
$t = 1;
$e = 1;
}
else
{
$year = $between>0? $year : ($year-1);
$yearMonth = self::getLunarYearMonths($year);
$leapMonth = self::getLeapMonth($year);
$between = $between>0?$between : (self::getLunarYearDays($year)+$between);
for($i=0;$i<13;$i++)
{
if($between==$yearMonth[$i])
{
$t=$i+2;
$e=1;
break;
}else if($between<$yearMonth[$i])
{
$t=$i+1;
$e=$between-(empty($yearMonth[$i-1])?0:$yearMonth[$i-1])+1;
break;
}
}
$m = ($leapMonth!=0&&$t==$leapMonth+1)?('闰'.self::getCapitalNum($t- 1,true)):self::getCapitalNum(($leapMonth!=0&&$leapMonth+1<$t?($t-1):$t),true);
array_push($lunarArray,$year,$m,self::getCapitalNum($e,false));
}
array_push($lunarArray,self::getLunarYearName($year));// 天干地支
array_push($lunarArray,$t,$e);
array_push($lunarArray,self::getYearZodiac($year));// 12生肖
array_push($lunarArray,$leapMonth);// 闰几月
return $lunarArray;
}
/**
* 获取数字的阴历叫法
* @param num 数字
* @param isMonth 是否是月份的数字
*/
public static function getCapitalNum($num,$isMonth=false)
{
$dateHash=array('0'=>'','1'=>'一','2'=>'二','3'=>'三','4'=>'四','5'=>'五','6'=>'六','7'=>'七','8'=>'八','9'=>'九','10'=>'十 ');
$monthHash=array('0'=>'','1'=>'正月','2'=>'二月','3'=>'三月','4'=>'四月','5'=>'五月','6'=>'六月','7'=>'七月','8'=>'八月','9'=>'九月','10'=>'十月','11'=>'冬月','12'=>'腊月');
$res='';
if($isMonth) $res = $monthHash[$num];
else
{
if($num<=10) $res = '初'.$dateHash[$num];
else if($num>10&&$num<20) $res = '十'.$dateHash[$num-10];
else if($num==20) $res = "二十";
else if($num>20&&$num<30) $res = "廿".$dateHash[$num-20];
else if($num==30) $res = "三十";
}
return $res;
}
/**
* 节气通用算法
* @param string $date
* @return array
*/
public static function getJieQi($date='')
{
$date = $date?date('Y-n-j', strtotime($date)):date('Y-n-j');
$dateArr = explode('-', $date);
$year = substr($dateArr[0],-2)+0;
$coefficient = array(
array(5.4055,2019,-1),//小寒
array(20.12,2082,1),//大寒
array(3.87),//立春
array(18.74,2026,-1),//雨水
array(5.63),//惊蛰
array(20.646,2084,1),//春分
array(4.81),//清明
array(20.1),//谷雨
array(5.52,1911,1),//立夏
array(21.04,2008,1),//小满
array(5.678,1902,1),//芒种
array(21.37,1928,1),//夏至
array(7.108,2016,1),//小暑
array(22.83,1922,1),//大暑
array(7.5,2002,1),//立秋
array(23.13),//处暑
array(7.646,1927,1),//白露
array(23.042,1942,1),//秋分
array(8.318),//寒露
array(23.438,2089,1),//霜降
array(7.438,2089,1),//立冬
array(22.36,1978,1),//小雪
array(7.18,1954,1),//大雪
array(21.94,2021,-1)//冬至
);
$term_name = array(
"小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨",
"立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑",
"白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至");
$idx1 = ($dateArr[1]-1)*2;
$_leap_value = floor(($year-1)/4);
$day1 = floor($year*0.2422+$coefficient[$idx1][0])-$_leap_value;
if(isset($coefficient[$idx1][1])&&$coefficient[$idx1][1]==$dateArr[0]) $day1 += $coefficient[$idx1][2];
$day2 = floor($year*0.2422+$coefficient[$idx1+1][0])-$_leap_value;
if(isset($coefficient[$idx1+1][1])&&$coefficient[$idx1+1][1]==$dateArr[0]) $day1 += $coefficient[$idx1+1][2];
//echo __FILE__.'->'.__LINE__.' $day1='.$day1,',$day2='.$day2.'<br/>'.chr(10);
$data=[];
if($dateArr[2]<$day1){
$data['name1']=$term_name[$idx1-1];
$data['name2']=$term_name[$idx1-1].'后';
}else if($dateArr[2]==$day1){
$data['name1']=$term_name[$idx1];
$data['name2']=$term_name[$idx1];
}else if($dateArr[2]>$day1 && $dateArr[2]<$day2){
$data['name1']=$term_name[$idx1];
$data['name2']=$term_name[$idx1].'后';
}else if($dateArr[2]==$day2){
$data['name1']=$term_name[$idx1+1];
$data['name2']=$term_name[$idx1+1];
}else if($dateArr[2]>$day2){
$data['name1']=$term_name[$idx1+1];
$data['name2']=$term_name[$idx1+1].'后';
}
return $data;
}
/**
* 获取节日:特殊的节日只能修改此函数来计算
* @param $today
* @param false $nl_info
* @param int $config
* @return string
*/
public static function getFestival($today='', $nl_info=false, $config = 1)
{
$today = $today?date('Y-n-j', strtotime($today)):date('Y-n-j');
if($config == 1){
$arr_lunar=array('01-01'=>'春节','01-15'=>'元宵节','02-02'=>'二月二','05-05'=>'端午节','07-07'=>'七夕节','08-15'=>'中秋节','09-09'=>'重阳节','12-08'=>'腊八节','12-23'=>'小年');
$arr_solar=array('01-01'=>'元旦','02-14'=>'情人节','03-12'=>'植树节','04-01'=>'愚人节','05-01'=>'劳动节','06-01'=>'儿童节','10-01'=>'国庆节','10-31'=>'万圣节','12-24'=>'平安夜','12-25'=>'圣诞节');
}
//需要不同节日的,用不同的$config,然后配置$arr_lunar和$arr_solar
$festivals = [];
list($y,$m,$d) = explode('-',$today);
if(!$nl_info) $nl_info = self::convertSolarToLunar($today);
if($nl_info['info'][7]>0&&$nl_info['info'][7]<$nl_info['info'][4]) $nl_info['info'][4]-=1;
$md_lunar = substr('0'.$nl_info['info'][4],-2).'-'.substr('0'.$nl_info['info'][5],-2);
$md_solar=substr_replace($today,'',0,5);
isset($arr_lunar[$md_lunar])?array_push($festivals, $arr_lunar[$md_lunar]):'';
isset($arr_solar[$md_solar])?array_push($festivals, $arr_solar[$md_solar]):'';
$glweek = date("w",strtotime($today)); //0-6
if($m==5&&($d>7)&&($d<15)&&($glweek==0))array_push($festivals, "母亲节");
if($m==6&&($d>14)&&($d<22)&&($glweek==0))array_push($festivals,"父亲节");
$jieqi = self::getJieQi($today);
$jieqi = isset($jieqi)?$jieqi['name1']:'';
if($jieqi) array_push($festivals, $jieqi);
return implode('/',$festivals);
}
/**
* 获取当前时间属于哪个时辰
* @param $h
* @return string
*/
public static function getTheHour($h='')
{
$d=$h?$h:date('G');
if($d==23 || $d==0){
return '子时';
}else if($d==1 || $d==2){
return '丑时';
}else if($d==3 || $d==4){
return '寅时';
}else if($d==5 || $d==6){
return '卯时';
}else if($d==7 || $d==8){
return '辰时';
}else if($d==9 || $d==10){
return '巳时';
}else if($d==11 || $d==12){
return '午时';
}else if($d==13 || $d==14){
return '未时';
}else if($d==15 || $d==16){
return '申时';
}else if($d==17 || $d==18){
return '酉时';
}else if($d==19 || $d==20){
return '戌时';
}else if($d==21 || $d==22){
return '亥时';
}
}
}
项目中常用到生日提醒,且生日通常为农历生日。
本接口使用有限,推荐自定义转化类,链接:PHP阳历转阴历(农历)类,支持生肖、时辰等老黄历
基础方法:
//获取农历生日
function getNongLi($day='')
{
$day = $day?:date('Y-m-d');
$nongli = \sensen\services\HttpService::getRequest('https://api.xlongwei.com/service/datetime/convert.json?day='.$day);
$nongli = json_decode($nongli, true);
$end = $nongli['lunarYear'].'-'.$nongli['lunarMonth'].'-'.$nongli['lunarDay'];
$end = date('m-d', strtotime("{$end}"));
return $end;
}
//获取阳历生日
function getYangLi($day='')
{
if(!$day) return '';
$arr = explode('-', $day);
$nongli = \sensen\services\HttpService::getRequest("https://api.xlongwei.com/service/datetime/yangli.json?lunarYear={$arr[0]}&lunarMonth={$arr[1]}&lunarDay={$arr[2]}");
$nongli = json_decode($nongli, true);
if(isset($nongli['status'])) return '';
return $nongli['day'];
}
项目日志列表中需对报错数据详细信息进行查阅,需对json格式进行美化,如图:
样式代码:
<style>
.key{
color: red;
}
.string{
color: green;
}
.layui-layer-content{
padding: 10px;
}
</style>
layer弹窗展示数据:
table.on('tool(table-toolbar)', function(obj){
var data = obj.data;
var str = prettyFormat(data.data);
if(obj.event === 'detail'){
layer.open({
type: 1,
title: '数据详情',
skin: 'layui-layer-rim', //加上边框
area: ['600px', '500px'], //宽高
content: str
});
}
});
//json格式美化
function prettyFormat(json) {
try {
// 设置缩进为2个空格
//str = JSON.stringify(JSON.parse(str), null, 2);
str = JSON.stringify(json, null, 2);
str = str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
str = str.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
return "<pre>"+str+"</pre>"
} catch (e) {
alert("异常信息:" + e);
}
}
实际项目中使用curl进行数据请求,访问速度异常的慢,基本上要5s以上的时间,最终通过以下设置得以解决。
//curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); //强制协议为1.0
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect:']); //头部要送出'Expect: '
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 ); //强制使用IPV4协议解析域名
快速获取RSA密钥对:http://www.metools.info/code/c80.html
示例代码:
<?php
class App extends BaseController
{
private $syncUrl = 'https://www.xxx.com/api/event/sync';
private $privateKey; //私钥
private $publicKey; //其他系统公钥
private $keyLen = 2048;
private $appId = 1;
private $identity = 'hello';
public function __construct()
{
$this->privateKey = file_get_contents((app()->getRootPath() . 'private_key.pam'));
$this->publicKey = file_get_contents((app()->getRootPath() . 'cert_public.pam'));
}
//新版接口
public function test($uid='', $token='')
{
//请求tokenIsValidSync判断请求是否合法
$data = [
'app_id'=>$this->appId,
'app_identity'=>$this->identity,
'timestamp'=>time(),
'rnd_str'=>uniqid(),
'event'=>'tokenIsValidSync',
'uid'=>$uid,
'token'=>$token,
'profile'=>1
];
$enData = $this->privateEncrypt($data);
$postData = [
'app_id'=>$this->appId,
'data'=>$enData
];
$ret = HttpService::postRequest($this->syncUrl, $postData);
$info = json_decode($ret, true);
$decryptData = $this->publicDecrypt($info['data']);
$userInfo = json_decode($decryptData, true);
}
/**
* 私钥加密
*
* @param string|array $data 待加密数据
* @return string 已加密的内容
*/
public function privateEncrypt($data)
{
//如果是数组,则转换为JSON字符串
if (is_array($data)) $data = json_encode($data);
if (!is_string($data)) {
return null;
}
$encrypted = '';
//采用默认的 OPENSSL_PKCS1_PADDING 填充格式,数据长度必须小于密钥长度 - 11
$part_len = $this->keyLen / 8 - 11;
$parts = str_split($data, $part_len);
foreach ($parts as $part) {
$encrypted_temp = '';
openssl_private_encrypt($part, $encrypted_temp, $this->privateKey);
$encrypted .= $encrypted_temp;
}
return base64_encode($encrypted);
}
/**
* 公钥解密
*
* @param string $encrypted 待解密内容
* @return string 已解密内容
*/
public function publicDecrypt($encrypted = '')
{
if (!is_string($encrypted)) {
return null;
}
$decrypted = '';
$part_len = $this->keyLen / 8;
$base64_decoded = base64_decode($encrypted);
$parts = str_split($base64_decoded, $part_len);
foreach ($parts as $part) {
$decrypted_temp = '';
openssl_public_decrypt($part, $decrypted_temp, $this->publicKey);
$decrypted .= $decrypted_temp;
}
return $decrypted;
}
}
前端参考代码如下,后台PHP代码参考上篇文章。
其中,key为后台动态生成的16位随机字符串并写入cache中,登录验证verify方法中需先判断此key的缓存是否存在。
引入相关js:
<script src="{__PLUG_PATH}crypto-js/core.js"></script>
<script src="{__PLUG_PATH}crypto-js/cipher-core.js"></script>
<script src="{__PLUG_PATH}crypto-js/aes.js"></script>
<script src="{__PLUG_PATH}crypto-js/mode-ecb.js"></script>
<script src="{__PLUG_PATH}crypto-js/enc-base64.js"></script>
登录操作:
form.on('submit(login)', function(obj){
let data = obj.field;
let key = CryptoJS.enc.Utf8.parse(data.key);
let password = CryptoJS.enc.Utf8.parse(data.password);
let encrypted = CryptoJS.AES.encrypt(password, key, {iv:key, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7});
var hexStr = encrypted.ciphertext.toString();
var oldHexStr = CryptoJS.enc.Hex.parse(hexStr);
let base64 = CryptoJS.enc.Base64.stringify(oldHexStr);
data.password = base64;
$.ajax({
url: "{:url('verify')}"
,data: data
,method: "POST"
,dataType: "json"
,success: function(res){
if(res.code == 200){
layer.msg('登录成功', {time: 1000}, function(){
location.href = "{:url('index/index')}";
});
}else{
//重置验证码
$('#vercode').trigger('click');
$('#code').val('');
layer.msg(res.msg, {time: 2000});
}
}
});
});
<?php
/**
* Desc: AES加密解密
* User: SenSen Wechat:1050575278
* Date: 2021/4/27
* Time: 14:14
*/
namespace sensen\services;
class AesService
{
protected static $method = "AES-128-CBC";
const KEY = 'fei1zui2you3jie!';
const IV = 'hello202105world';
/**
* 加密
* @param string|array $data 待加密
* @param string $key 秘钥
* @param string $iv 偏移量
* @return string|string[]|array
*/
public static function encrypt($data, $key=self::KEY, $iv=self::IV)
{
if(is_array($data)){
$res = [];
foreach ($data as $v){
$text = openssl_encrypt($v, static::$method, $key, OPENSSL_RAW_DATA, $iv);
$res[] = self::safetyBase64Encode($iv . $text);
}
}else{
$text = openssl_encrypt($data, static::$method, $key, OPENSSL_RAW_DATA, $iv);
$res = self::safetyBase64Encode($iv . $text);
}
return $res;
}
/**
* 解密
* @param string|array $text 待解密
* @param string $key 秘钥
* @param string $iv 偏移量
* @param false $login 是否为登录
* @return false|string|array
*/
public static function decrypt($text, $key=self::KEY, $iv=self::IV, $login=false) {
if(is_array($text)){
$data = [];
foreach ($text as $v){
$cipherText = self::safetyBase64Decode($v);
$cipherText = substr($cipherText, strlen($iv));
$data[] = openssl_decrypt($cipherText, static::$method, $key, OPENSSL_RAW_DATA, $iv);
}
}else{
$cipherText = self::safetyBase64Decode($text);
if($login){
//仅方便登录使用,登录时iv与key相同
$data = openssl_decrypt(base64_decode($text), static::$method, $key, OPENSSL_RAW_DATA, $iv);
}else{
$cipherText = substr($cipherText, strlen($iv));
$data = openssl_decrypt($cipherText, static::$method, $key, OPENSSL_RAW_DATA, $iv);
}
}
return $data;
}
/**
* base64安全编码
* @param string $text
* @return string|string[]
*/
public static function safetyBase64Encode($text)
{
$text = base64_encode($text);
$text = str_replace(['+','/'],['-','_'],$text);
return $text;
}
/**
* base64安全解码
* @param string $text
* @return false|string
*/
public static function safetyBase64Decode($text)
{
$text = str_replace(['-','_'],['+','/'],$text);
$text = base64_decode($text);
return $text;
}
}