在项目中遇到客户一个需求,ID的生成策略:长度8位,用户在知道自己的ID(如:10000018)后,不能通过ID来推测相关信息,如用户可能推测我是第18个注册用户,上一个注册用户的ID是10000017,这样就不能很好的保护账号安全了,可以凭证推测数据来进行不法之事。
经过网上查找和思考,便有了如下的解决方案:
- 生成一个装有1-99999999数值的数组
- 将数值内的值随机打乱
- 将数值的值分开保存到不同的文件(txt)中
- 获取ID的时候,可以随机从任意一文件中获取第一行的ID值,然后将剩余行的ID值重写写会原文件
- 如果文件中的ID值已经取完则删除
逻辑很简单,就看实现了,代码如下:
生成ID并保存到硬盘
/**
* 生成1-99999999的数字编码保存到硬盘
*/
private static void generateAppCode2Disk(){
int begin = 1;
int end = 99999999;
int count = begin + end;
//生成1到99999999的所有整数
int[] codes = new int[count + 1];
for (int i = begin; i <= end; i++){
codes[i] = i;
}
//随机交换数据
int index = 0;
int tempCode = 0;
Random random = new Random();
for (int i = begin; i <= end; i++){
index = random.nextInt(count+1);
tempCode = codes[index];
codes[index] = codes[i];
codes[i] = tempCode;
}
//生成1000个文件,每个文件包含100000个appCode
StringBuilder sb = new StringBuilder();
int flag = 100000;
System.out.println("***********开始**********");
try {
for(int i = begin; i <= end; i++){
sb.append(codes[i]).append("\n");
if(i == end || i%flag == 0){
File folder = new File("D:/IDGenerate");
if(!folder.isDirectory()){
folder.mkdir();
}
if(i==end){
i = end +1;
}
File file = new File("D:/IDGenerate/ID_"+(i/flag)+".txt");
if (!file.exists()) {
file.createNewFile();
}
BufferedWriter bw=new BufferedWriter(new FileWriter(file.getAbsoluteFile()));
bw.write(sb.toString());
bw.flush();
bw.close();
sb = new StringBuilder();
System.out.println("当前i值:"+i+"第"+(i/flag)+"个文件生成成功!");
}
}
System.out.println("***********结束**********");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
获取ID
//获取唯一8位ID
public String createAppCode(){
BufferedWriter bw = null;
BufferedReader br = null;
FileReader fr = null;
FileWriter fw = null;
try {
String dir = "D:/";
if(StringUtils.isBlank(dir)){
throw new Exception("获取文件路径为空");
}
File rootFile = new File(dir);
String[] fileNames = rootFile.list();
if(fileNames == null || fileNames.length == 0){
throw new Exception("路径不正确,或者ID已经分配完毕,请联系管理员");
}
//获取第一个文件
fr=new FileReader(dir+"/"+fileNames[0]);//获取文件流
br = new BufferedReader(fr); //将流整体读取。
StringBuilder sb = new StringBuilder();
String appCode = "";
String temp;
int count =1;
while(!StringUtils.isBlank(temp=br.readLine())){
if(count == 1){
count++;
appCode = temp;
continue;
}
else{
sb.append(temp).append("\n");
}
}
br.close();
fr.close();
if(!StringUtils.isBlank(appCode)){
//判断文件内容是否还有下一行
if(sb.length()<=0){
File delFile = new File(dir+"/"+fileNames[0]);
if(delFile.exists()){
delFile.delete();//删掉
}
}
else{
//将剩余内容重写写回文件
fw = new FileWriter(dir+"/"+fileNames[0]);
bw=new BufferedWriter(fw);
bw.write(sb.toString());
bw.flush();
bw.close();
fw.close();
}
String prex = "00000000";
appCode = prex.substring(0,prex.length()-appCode.length())+appCode;
return appCode;
}
else{
throw new Exception("文件中内容为空");
}
} catch (Exception e) {
log.error("获取ID error:"+e.getMessage());
return null;
} finally{
try {
if(bw != null)bw.close();
if(br != null)bw.close();
if(fr != null)bw.close();
if(fw != null)bw.close();
} catch (IOException e) {
log.error("关闭文件流文件异常:"+e.getMessage());
}
}
}
这样将生成的ID分成1000个文件保存到硬盘,取数据的时候从任意文件中取都可,这样能很好的保证了随机,不重复,而且在测试的时候发现效率还不错,内存,CPU的使用都微乎其微。
为了更好的优化获取ID效率,可以有如下改进:
- 将ID保存到更多的文件中,这样文件的容量就可以很小,加载的时候就更快
预读ID,可以提前预读50,100等数量的ID保存到list中,直接从内存取数据就更快了,不过这逻辑就复杂了很多,要保证list的数据和硬盘数据的同步,就需要更多的代码来判断了
此文章供大家参考,大家有什么好的建议,算法都可以提出来交流的!