vip激活码、兑换码生成工具类,支持校验

        最近做了一个这样的需求,系统有一部分vip用户不需要普通用户购买,而是公司统一购买兑换码,公司员工输入自己的兑换码,如果兑换码正确的话,自动升级为vip用户,兑换码要数字和大写字母,不能重复,这里记录一个现场的工具类

package com.leliven.washcar.member.util;

import cn.hutool.core.util.RandomUtil;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * 激活码生成工具
 *
 * @author ycf
 * @since 2021-12-21
 */
public class ActivationCodeUtil {
	
	static String stringTable = "ABCDEFGHIJKMNPQRSTUVWXYZ23456789";
	final static String password = "dak3le2";
	
	//从byte转为字符表索引所需要的位数
	final static int convertByteCount = 5;
	
	public static void main( String[] args ) throws Exception {
	//	System.out.println(RandomUtil.randomInt(1, 100));

		ShowTime();
		System.out.println("=======================");
		create((byte) RandomUtil.randomInt(1,100),10,12,password);

		VerifyCode("2CZNXWNM2KZA");
		VerifyCode("HIYGXFWM74MA");
		VerifyCode("CUT2W36ICK4A");
		VerifyCode("M3MKCPJFMBVA");
		VerifyCode("M3MKCPJFMTVA");
    }
 
	/**
	 * 生成兑换码
	 * 这里每一次生成兑换码的最大数量为int的最大值即2147483647
	 * @return
	 */
	public static List<String> create(byte groupId, int codeCount, int codeLength, String password) {
		List<String> codes = new ArrayList<>();

		//8位的数据总长度
		int fullCodeLength = codeLength * convertByteCount / 8;
		//随机码对时间和id同时做异或处理
		//类型1,id4,随机码n,校验码1 
		int randCount = fullCodeLength - 6;//随机码有多少个
		
		//如果随机码小于0 不生成
		if(randCount <= 0 ) {
			return codes;
		}
		for(int i = 0 ; i < codeCount ; i ++) {
			//这里使用i作为code的id
			//生成n位随机码
			byte[] randBytes = new byte[randCount];
			for(int j = 0 ; j  < randCount ; j ++) {
				randBytes[j] = (byte)(Math.random() * Byte.MAX_VALUE);
			}
 
			//存储所有数据
			ByteHelper byteHelper = ByteHelper.CreateBytes(fullCodeLength);
			byteHelper.AppendNumber(groupId).AppendNumber(i).AppendBytes(randBytes);
 
			//计算校验码 这里使用所有数据相加的总和与byte.max 取余
			byte verify = (byte) (byteHelper.GetSum() % Byte.MAX_VALUE);
			byteHelper.AppendNumber(verify);
 
			//使用随机码与时间和ID进行异或
			for(int j = 0 ; j < 5 ; j ++) {
				byteHelper.bytes[j] = (byte) (byteHelper.bytes[j] ^ (byteHelper.bytes[5 + j % randCount]));
			}
 
			//使用密码与所有数据进行异或来加密数据
			byte[] passwordBytes = password.getBytes();
			for(int j = 0; j < byteHelper.bytes.length ; j++){
				byteHelper.bytes[j] = (byte) (byteHelper.bytes[j] ^ passwordBytes[j % passwordBytes.length]);
			}
			
			//这里存储最终的数据
			byte[] bytes = new byte[codeLength];
			
			//按6位一组复制给最终数组
			for(int j = 0; j < byteHelper.bytes.length ; j ++) {
				for(int k = 0 ; k < 8 ; k ++) {
					int sourceIndex = j*8+k;
					int targetIndex_x = sourceIndex / convertByteCount;
					int targetIndex_y = sourceIndex % convertByteCount;
					byte placeVal = (byte)Math.pow(2, k);
					byte val = (byte)((byteHelper.bytes[j] & placeVal) == placeVal ? 1:0);
					//复制每一个bit
					bytes[targetIndex_x] = (byte)(bytes[targetIndex_x] | (val << targetIndex_y));
				}
			}
			
			StringBuilder result = new StringBuilder();
			//编辑最终数组生成字符串
			for(int j = 0 ; j < bytes.length ; j ++) {
				result.append(stringTable.charAt(bytes[j]));
			}
			codes.add(result.toString());
		}
		ShowTime();
		return codes;
	}

	
	/**
	 * 验证兑换码
	 * @param code
	 */
	public static boolean VerifyCode(String code ){
		byte[] bytes = new byte[code.length()];
		
		//首先遍历字符串从字符表中获取相应的二进制数据
		for(int i=0;i<code.length();i++){
		    byte index = (byte) stringTable.indexOf(code.charAt(i));
		    bytes[i] = index;
		}
		
		//还原数组
		int fullCodeLength = code.length() * convertByteCount / 8;
		int randCount = fullCodeLength - 6;//随机码有多少个
		
		byte[] fullBytes = new byte[fullCodeLength];
		for(int j = 0 ; j < fullBytes.length ; j ++) {
			for(int k = 0 ; k < 8 ; k ++) {
				int sourceIndex = j*8+k;
				int targetIndex_x = sourceIndex / convertByteCount;
				int targetIndex_y = sourceIndex % convertByteCount;
				
				byte placeVal = (byte)Math.pow(2, targetIndex_y);
				byte val = (byte)((bytes[targetIndex_x] & placeVal) == placeVal ? 1:0);
				
				fullBytes[j] = (byte) (fullBytes[j] | (val << k));
			}
		}
 
		//解密,使用密码与所有数据进行异或来加密数据
		byte[] passwordBytes = password.getBytes();
		for(int j = 0 ; j < fullBytes.length ; j++){
			fullBytes[j] = (byte) (fullBytes[j] ^ passwordBytes[j % passwordBytes.length]);
		}
 
		//使用随机码与时间和ID进行异或
		for(int j = 0 ; j < 5 ; j ++) {
			fullBytes[j] = (byte) (fullBytes[j] ^ (fullBytes[5 + j % randCount]));
		}
		
		//获取校验码 计算除校验码位以外所有位的总和
		int sum = 0;
		for(int i = 0 ;i < fullBytes.length - 1; i ++){
			sum += fullBytes[i];
		}
		byte verify = (byte) (sum % Byte.MAX_VALUE);
		
		//校验
		if(verify == fullBytes[fullBytes.length - 1]){
			System.out.println(code + " : verify success!");
			return true;
		}else {
			System.out.println(code + " : verify failed!");
			return false;
		}
		
	}
	
	
	
	public static void ShowTime(){
		Date date = new Date();
		long times = date.getTime();//时间戳
		System.out.println("time  : " + times);
	}
}

辅助工具类:

package com.leliven.washcar.member.util;

public class ByteHelper {
	//原始数组
	byte[] bytes;
	//记录当前写入到多少位
	int index;
	
	private ByteHelper(int capacity){
		bytes = new byte[capacity];
		index = 0;
	}
	
	public static ByteHelper CreateBytes(int capacity){
		ByteHelper byteHelper = new ByteHelper(capacity);
		return byteHelper;
	}
	
	//向数组中追加内容
	public ByteHelper AppendNumber(long val){
		byte[] arr = Number2byte(val);
		AppendBytes(arr);
		return this;
	}
	public ByteHelper AppendNumber(int val){
		byte[] arr = Number2byte(val);
		AppendBytes(arr);
		return this;
	}
	public ByteHelper AppendNumber(short val){
		byte[] arr = Number2byte(val);
		AppendBytes(arr);
		return this;
	}
	public ByteHelper AppendNumber(byte val){
		byte[] arr = new byte[]{val};
		AppendBytes(arr);
		return this;
	}
	
	/**
	 * 获取数据的总和
	 * @return
	 */
	public int GetSum(){
		int ret = 0;
		for(int i = 0 ; i < bytes.length ; i ++){
			ret += bytes[i];
		}
		return ret;
	}
	
	//追加byte数组
	public ByteHelper AppendBytes(byte[] arr){
		
		for(byte i = 0 ; i < arr.length ; i ++){
			bytes[index + i] = arr[i];
		}
		
		index += arr.length;
		return this;
	}
	
	/**
     * 将数字转换为byte数组
     */
    public static byte[] Number2byte(long val) {
    	
        byte[] arr = new byte[]{
        		(byte) ((val >> 56) & 0xFF),   
		        (byte) ((val >> 48) & 0xFF),      
		        (byte) ((val >> 40) & 0xFF),         
		        (byte) ((val >> 32) & 0xFF), 
                (byte) ((val >> 24) & 0xFF),   
		        (byte) ((val >> 16) & 0xFF),      
		        (byte) ((val >> 8) & 0xFF),      
		        (byte) (val & 0xFF) 	
        };
        
        return arr;
    }
    public static byte[] Number2byte(int val) {
    	
        byte[] arr = new byte[]{
                (byte) ((val >> 24) & 0xFF),   
		        (byte) ((val >> 16) & 0xFF),      
		        (byte) ((val >> 8) & 0xFF),      
		        (byte) (val & 0xFF) 	
        };
        
        return arr;
    }
    public static byte[] Number2byte(short val) {
    	
        byte[] arr = new byte[]{     
		        (byte) ((val >> 8) & 0xFF),      
		        (byte) (val & 0xFF) 	
        };
        
        return arr;
    }
}

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值