当前位置: 首页 > news >正文

兑换码生成与解析-个人笔记(java)

1.需求分析

  • 兑换码长度为10字符,包含24个大写字母和8个数字。
  • 兑换码需要保证唯一性,不可重复兑换。
  • 需要防止猜测和爆刷攻击。
  • 兑换码生成和验证的算法需要高效,避免对数据库带来较大的压力。

    导航

    • 1.需求分析
    • 2.实现方案
    • 3.加密过程
    • 4.解密过程
    • 5.工具类
      • 5.1Base32工具类
      • 5.2CodeUtil工具类
        • 5.2.1.兑换码算法说明:
        • 5.2.2.兑换码的明文结构:
        • 5.2.3.兑换码的加密过程:
        • 5.2.4.兑换码的解密过程:

2.实现方案

  • 使用自增ID作为唯一ID,长度为32位整数。
  • 使用Base32编码将每5位二进制转换为一个字符。
  • 使用按位加权签名和随机新鲜值来防止刷取攻击

3.加密过程

  • 生成4位新鲜值
  • 拼接新鲜值和序列号得到载荷
  • 用新鲜值选择加权数组计算校验码
  • 用校验码选择异或密钥异或混淆载荷
  • 拼接校验码和混淆载荷,Base32编码

4.解密过程

  • Base32解码得到数值
  • 分离校验码和载荷
  • 用校验码选择异或密钥恢复载荷
  • 重新计算校验码验证一致性

5.工具类

  • Base32:用于编码和解码算法。
  • CodeUtil:用于生成和解析兑换码。

5.1Base32工具类

代码如下:

import cn.hutool.core.text.StrBuilder;/**
* 将整数转为base32字符的工具,因为是32进制,所以每5个bit位转一次
*/
public class Base32 {private final static String baseChars = "6CSB7H8DAKXZF3N95RTMVUQG2YE4JWPL";public static String encode(long raw) {StrBuilder sb = new StrBuilder();while (raw != 0) {int i = (int) (raw & 0b11111);sb.append(baseChars.charAt(i));raw = raw >>> 5;}return sb.toString();}public static long decode(String code) {long r = 0;char[] chars = code.toCharArray();for (int i = chars.length - 1; i >= 0; i--) {long n = baseChars.indexOf(chars[i]);r = r | (n << (5*i));}return r;}public static String encode(byte[] raw) {StrBuilder sb = new StrBuilder();int size = 0;int temp = 0;for (byte b : raw) {if (size == 0) {// 取5个bitint index = (b >>> 3) & 0b11111;sb.append(baseChars.charAt(index));// 还剩下3位size = 3;temp = b & 0b111;} else {int index = temp << (5 - size) | (b >>> (3 + size) & ((1 << 5 - size) - 1)) ;sb.append(baseChars.charAt(index));int left = 3 + size;size = 0;if(left >= 5){index = b >>> (left - 5) & ((1 << 5) - 1);sb.append(baseChars.charAt(index));left = left - 5;}if(left == 0){continue;}temp = b & ((1 << left) - 1);size = left;}}if(size > 0){sb.append(baseChars.charAt(temp));}return sb.toString();}public static byte[] decode2Byte(String code) {char[] chars = code.toCharArray();byte[] bytes = new byte[(code.length() * 5 )/ 8];byte tmp = 0;byte byteSize = 0;int index = 0;int i = 0;for (char c : chars) {byte n = (byte) baseChars.indexOf(c);i++;if (byteSize == 0) {tmp = n;byteSize = 5;} else {int left = Math.min(8 - byteSize, 5);if(i == chars.length){bytes[index] =(byte) (tmp << left | (n & ((1 << left) - 1)));break;}tmp = (byte) (tmp << left | (n >>> (5 - left)));byteSize += left;if (byteSize >= 8) {bytes[index++] = tmp;byteSize = (byte) (5 - left);if (byteSize == 0) {tmp = 0;} else {tmp = (byte) (n & ((1 << byteSize) - 1));}}}}return bytes;}
}

5.2CodeUtil工具类

5.2.1.兑换码算法说明:

  • ​ 兑换码分为明文和密文,明文是50位二进制数,密文是长度为10的Base32编码的字符串

5.2.2.兑换码的明文结构:

14(校验码) + 4 (新鲜值) + 32(序列号)

  • ​ 序列号:一个单调递增的数字,可以通过Redis来生成
  • ​ 新鲜值:可以是优惠券id的最后4位,同一张优惠券的兑换码就会有一个相同标记
  • ​ 载荷:将新鲜值(4位)拼接序列号(32位)得到载荷
  • ​ 校验码:将载荷4位一组,每组乘以加权数,最后累加求和,然后对2^14求余得到

5.2.3.兑换码的加密过程:

  1. ​ 首先利用优惠券id计算新鲜值 f
  2. ​ 将f和序列号s拼接,得到载荷payload
  3. ​ 然后以f为角标,从提前准备好的16组加权码表中选一组
  4. ​ 对payload做加权计算,得到校验码 c
  5. ​ 利用c的后4位做角标,从提前准备好的异或密钥表中选择一个密钥:key
  6. ​ 将payload与key做异或,作为新payload2
  7. ​ 然后拼接兑换码明文:f (4位) + payload2(36位)
  8. ​ 利用Base32对密文转码,生成兑换码

5.2.4.兑换码的解密过程:

  1. ​ 首先利用Base32解码兑换码,得到明文数值num
  2. ​ 取num的高14位得到c1,取num低36位得payload
  3. ​ 利用c1的后4位做角标,从提前准备好的异或密钥表中选择一个密钥:key
  4. ​ 将payload与key做异或,作为新payload2
  5. ​ 利用加密时的算法,用payload2和s1计算出新校验码c2,把c1和c2比较,一致则通过

代码如下:

import com.tianji.common.constants.RegexConstants;
import com.tianji.common.exceptions.BadRequestException;public class CodeUtil {/*** 异或密钥表,用于最后的数据混淆*/private final static long[] XOR_TABLE = {61261925471L, 61261925523L, 58169127203L, 64169927267L,64169927199L, 61261925629L, 58169127227L, 64169927363L,59169127063L, 64169927359L, 58169127291L, 61261925739L,59169127133L, 55139281911L, 56169127077L, 59169127167L};/*** fresh值的偏移位数*/private final static int FRESH_BIT_OFFSET = 32;/*** 校验码的偏移位数*/private final static int CHECK_CODE_BIT_OFFSET = 36;/*** fresh值的掩码,4位*/private final static int FRESH_MASK = 0xF;/*** 验证码的掩码,14位*/private final static int CHECK_CODE_MASK = 0b11111111111111;/*** 载荷的掩码,36位*/private final static long PAYLOAD_MASK = 0xFFFFFFFFFL;/*** 序列号掩码,32位*/private final static long SERIAL_NUM_MASK = 0xFFFFFFFFL;/*** 序列号加权运算的秘钥表*/private final static int[][] PRIME_TABLE = {{23, 59, 241, 61, 607, 67, 977, 1217, 1289, 1601},{79, 83, 107, 439, 313, 619, 911, 1049, 1237},{173, 211, 499, 673, 823, 941, 1039, 1213, 1429, 1259},{31, 293, 311, 349, 431, 577, 757, 883, 1009, 1657},{353, 23, 367, 499, 599, 661, 719, 929, 1301, 1511},{103, 179, 353, 467, 577, 691, 811, 947, 1153, 1453},{213, 439, 257, 313, 571, 619, 743, 829, 983, 1103},{31, 151, 241, 349, 607, 677, 769, 823, 967, 1049},{61, 83, 109, 137, 151, 521, 701, 827, 1123},{23, 61, 199, 223, 479, 647, 739, 811, 947, 1019},{31, 109, 311, 467, 613, 743, 821, 881, 1031, 1171},{41, 173, 367, 401, 569, 683, 761, 883, 1009, 1181},{127, 283, 467, 577, 661, 773, 881, 967, 1097, 1289},{59, 137, 257, 347, 439, 547, 641, 839, 977, 1009},{61, 199, 313, 421, 613, 739, 827, 941, 1087, 1307},{19, 127, 241, 353, 499, 607, 811, 919, 1031, 1301}};/*** 生成兑换码** @param serialNum 递增序列号* @return 兑换码*/public static String generateCode(long serialNum, long fresh) {// 1.计算新鲜值fresh = fresh & FRESH_MASK;// 2.拼接payload,fresh(4位) + serialNum(32位)long payload = fresh << FRESH_BIT_OFFSET | serialNum;// 3.计算验证码long checkCode = calcCheckCode(payload, (int) fresh);System.out.println("checkCode = " + checkCode);// 4.payload做大质数异或运算,混淆数据payload ^= XOR_TABLE[(int) (checkCode & FRESH_MASK)];// 5.拼接兑换码明文: 校验码(14位) + payload(36位)long code = checkCode << CHECK_CODE_BIT_OFFSET | payload;// 6.转码return Base32.encode(code);}private static long calcCheckCode(long payload, int fresh) {// 1.获取码表int[] table = PRIME_TABLE[fresh];// 2.生成校验码,payload每4位乘加权数,求和,取最后13位结果long sum = 0;int index = 0;while (payload > 0) {sum += (payload & 0xf) * table[index++];payload >>>= 4;}return sum & CHECK_CODE_MASK;}public static long parseCode(String code) {if (code == null || !code.matches(RegexConstants.COUPON_CODE_PATTERN)) {// 兑换码格式错误throw new BadRequestException("无效兑换码");}// 1.Base32解码long num = Base32.decode(code);// 2.获取低36位,payloadlong payload = num & PAYLOAD_MASK;// 3.获取高14位,校验码int checkCode = (int) (num >>> CHECK_CODE_BIT_OFFSET);// 4.载荷异或大质数,解析出原来的payloadpayload ^= XOR_TABLE[(checkCode & FRESH_MASK)];// 5.获取高4位,freshint fresh = (int) (payload >>> FRESH_BIT_OFFSET & FRESH_MASK);// 6.验证格式:if (calcCheckCode(payload, fresh) != checkCode) {throw new BadRequestException("无效兑换码");}return payload & SERIAL_NUM_MASK;}
}

相关文章:

兑换码生成与解析-个人笔记(java)

1.需求分析 兑换码长度为10字符&#xff0c;包含24个大写字母和8个数字。兑换码需要保证唯一性&#xff0c;不可重复兑换。需要防止猜测和爆刷攻击。兑换码生成和验证的算法需要高效&#xff0c;避免对数据库带来较大的压力。 导航 1.需求分析2.实现方案3.加密过程4.解密过程5…...

2023/10/25MySQL学习

外键约束 在子表添加外键后,不能在主表删除或更新记录,因为存在外键关联 删除外键,注意外键名称时我们添加外键时起的名称 使用cascade操作后,可以操作主表数据,并且子表的外键也会对应改变 set null的话,删除主表对应主键信息后,子表对应外键信息变为空 多表关系 创建中间表 可…...

网络协议--Ping程序

7.1 引言 “ping”这个名字源于声纳定位操作。Ping程序由Mike Muuss编写&#xff0c;目的是为了测试另一台主机是否可达。该程序发送一份ICMP回显请求报文给主机&#xff0c;并等待返回ICMP回显应答&#xff08;图6-3列出了所有的ICMP报文类型&#xff09;。 一般来说&#x…...

如何在 Azure 容器应用程序上部署具有 Elastic Observability 的 Hello World Web 应用程序

作者&#xff1a;Jonathan Simon Elastic Observability 是提供对正在运行的 Web 应用程序的可见性的最佳工具。 Microsoft Azure 容器应用程序是一个完全托管的环境&#xff0c;使你能够在无服务器平台上运行容器化应用程序&#xff0c;以便你的应用程序可以扩展和缩减。 这使…...

JAVA排序

再看各种排序前我们先了解一下什么叫 稳定性 比如一组数据arr[i]下标与arr[j下标]相等,arr[i]在前面,arr[j]在arr[i]后面,排序后这两个数据仍然是arr[i]在arr[j]前面,arr[j]在arr[i]后面,这就叫稳定 插入排序&#xff1a; 优势: 越有序查找速度越快 时间复杂度: O(N^2) 空间复…...

opencalib中lidar2camera安装记录

目录 一、opencalib安装 二、lidar2camera的安装 三、测试运行 四、出现过的问题 一、opencalib安装 代码地址&#xff1a;https://github.com/PJLab-ADG/SensorsCalibration/blob/master/README.md # pull docker image sudo docker pull scllovewkf/opencalib:v1 # Aft…...

整个自动驾驶小车001:概述

材料&#xff1a; 1&#xff0c;树梅派4b&#xff0c;作为主控&#xff0c;这个东西有linux系统&#xff0c;方便 2&#xff0c;HC-S104超声波模块&#xff0c;我有多个&#xff0c;不少于4个&#xff0c;我可以前后左右四个方向都搞一个 3&#xff0c;l298n模块&#xff0c;…...

windows本地搭建mmlspark分布式机器平台流程

文章目录 windows本地搭建mmlspark分布式机器平台流程安装环境pyspark环境spark环境java环境hadoop环境1.修改hadoop配置文件下的jdk地址为自己的实际地址2.修改bin文件离线环境jar包环境1mmlsprk第三方包jar包环境2参考代码我有话说其他问题记录概要参考文献windows本地搭建mm…...

深入探究 Next.js 中的 getServerSideProps 和 getStaticProps 用法及区别

引言&#xff1a; Next.js 是一个流行的 React 框架&#xff0c;它提供了许多强大的功能来简化服务器渲染&#xff08;SSR&#xff09;和静态生成&#xff08;SSG&#xff09;的过程。其中&#xff0c;getServerSideProps 和 getStaticProps 是两个重要的函数&#xff0c;用于在…...

餐饮业如何高效经营?赶紧闭眼抄这个方法!

在现代社会&#xff0c;餐饮业已经成为人们日常生活中不可或缺的一部分。为了提高食堂运营效率&#xff0c;满足不断增长的客户需求&#xff0c;智慧收银系统应运而生。 智慧收银系统帮助食堂业主更好地理解其客户&#xff0c;提高服务质量&#xff0c;优化库存管理&#xff0c…...

餐饮外卖小程序商城的作用是什么

随着互联网及线上餐饮的发展趋势&#xff0c;行业洗牌正在加速&#xff0c;并且对餐饮连锁门店提出更高要求&#xff0c;餐饮数字化转型加快&#xff0c;积极发展线上经营是不少餐饮商家的首选。这其中&#xff0c;餐饮外卖商城成为很多餐饮品牌的线上经营品牌&#xff0c;也是…...

nRF52832 SDK15.3.0 基于ble_app_uart demo FreeRTOS移植

参考资料&#xff1a;Nrf52832 freeOS系统移植_nrf5283操作系统-CSDN博客 这里把移植经验记录下来&#xff0c;供有需要的同学参考&#xff0c;有不对的地方也请大家批评指正。 把FreeRTOS移植到 nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\ble_app_uart工程&#xff…...

电厂数据可视化三维大屏展示平台加强企业安全防范

园区可视化大屏是一种新型的信息化手段&#xff0c;能够将园区内各项数据信息以图像的形式直观呈现在大屏幕上&#xff0c;便于管理员和员工进行实时监控、分析和决策。本文将从以下几个方面介绍园区可视化大屏的作用和应用。 VR数字孪生园区系统是通过将实际园区的各种数据和信…...

2246: 【区赛】【宁波32届小学生】最佳交换

目录 题目描述 输入 输出 样例输入 样例输出 提示 代码 题目描述 星星小朋友和 N-1 个小伙伴一起玩了一上午的纸牌游戏&#xff0c;星星总是能赢&#xff0c;气焰嚣张&#xff0c; 小伙伴们决定出道纸牌问题难倒星星&#xff0c;让他别再狂妄自大了&#xff0c;问题是这…...

Java面试记录

文章目录 1、final关键字2、synchronized关键字&#xff08;1&#xff09;synchronized的功能&#xff1a;&#xff08;2&#xff09;synchronized的底层实现原理&#xff1a; 3、Java中线程同步的实现方法&#xff08;1&#xff09;. 使用synchronized关键字&#xff1a;&…...

【数据库】聚集函数

聚集函数 聚集函数一览AVG() 函数COUNT() 函数MAX() 函数MIN() 函数SUM() 函数 组合聚集函数 聚集函数一览 我们需要汇总数据而不是实际检索&#xff0c;此时我们使用聚集函数进行处理&#xff1b; 聚集函数一览表如下&#xff1a; 函数说明AVG()返回平均值COUNT()返回数量总…...

【单元测试】--编写单元测试

一、编写第一个单元测试 编写第一个单元测试通常包括以下步骤。以下示例以C#和NUnit为例&#xff1a; 创建测试项目&#xff1a; 在Visual Studio中&#xff0c;创建一个新的Class Library项目&#xff0c;这将是你的单元测试项目。在解决方案资源管理器中&#xff0c;右键点…...

ES(elasticsearch) - 三种姿势进行分页查询

1. from size 浅分页 "浅"分页可以理解为简单意义上的分页。它的原理很简单&#xff0c;就是查询前20条数据&#xff0c;然后截断前10条&#xff0c;只返回10-20的数据。这样其实白白浪费了前10条的查询。 GET test_dev/_search {"query": {"bool&…...

AQS是什么?AbstractQueuedSynchronizer之AQS原理及源码深度分析

文章目录 一、AQS概述1、什么是AQS2、技术解释3、基本原理4、AQS为什么这么重要 二、AQS数据结构1、AQS的结构2、ReentrantLock与AbstractQueuedSynchronizer3、AQS的state变量4、AQS的队列5、AQS的Node&#xff08;1&#xff09;Node的waitStatus&#xff08;2&#xff09;属性…...

【数据库】函数处理(文本处理函数、日期和时间处理函数、数值处理函数)

函数处理数据 算术运算函数文本处理函数日期和时间处理函数数值处理函数 算术运算 操作符说明加-减*乘/除 e . g . e.g. e.g. 列出 Orders 表中所有每项物品的 id&#xff0c;数量 quantity&#xff0c;单价 item_price&#xff0c;总价 expanded_price&#xff08;数量 * 单价…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...