如何避免token被截获后的重放攻击(Java)

  • token 加密,解密工具:
import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.Base64;

/**
 * @author visy.wang
 * @desc Token加密、解密工具
 * @date 2020/10/29 15:26
 */
public class TokenUtil {
	/**
	 * 密码和时间戳生成可传输的密文
	 * @param password 密码
	 * @param mapper 时间戳乱序的映射关系
	 * @return 密文
	 */
	public static String encode(String password, int[] mapper){
		String timestamp = String.valueOf(System.currentTimeMillis());

		int tLen = timestamp.length();
		timestamp = tLen>13 ? timestamp.substring(tLen-13) : timestamp;
		timestamp = Base64.getEncoder().encodeToString(timestamp.getBytes());
		tLen = timestamp.length();

		password = Base64.getEncoder().encodeToString(password.getBytes());
		int pLen = password.length();

		int j = 0, k = 0;
		char[] result = new char[tLen + pLen];
		for(int i=0; i<result.length; i++){
			if((i%2==0 && j<tLen) || k>=pLen){
				result[i] = timestamp.charAt(mapper[j]);
				j++;
			}else{
				result[i] = password.charAt(k);
				k++;
			}
		}

		return String.valueOf(result);
	}

	/**
	 * 密文解密出时间戳和密码
	 * @param token 密文
	 * @param mapper 时间戳乱序映射关系
	 * @return Token对象
	 */
	public static Token decode(String token, int[] mapper){
		int j = 0, k = 0;
		int len = token.length(), tLen = mapper.length, pLen = len-tLen;
		char[] timestamp = new char[tLen], password = new char[pLen];
		for(int i=0; i<len; i++){
			if((i%2==0 && j<tLen) || k>=pLen){
				timestamp[mapper[j]] = token.charAt(i);
				j++;
			}else{
				password[k] = token.charAt(i);
				k++;
			}
		}

		String time = String.valueOf(timestamp);
		String pwd = String.valueOf(password);
		time = new String(Base64.getDecoder().decode(time));
		pwd = new String(Base64.getDecoder().decode(pwd));

		return new Token(Long.valueOf(time), pwd);
	}

	@Data
	@AllArgsConstructor
	static class Token{
		private long timestamp;

		private String password;
	}
}
  • 测试:
import lombok.AllArgsConstructor;
import lombok.Data;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.util.*;

public class Test{
	/**
	 * 缓存上一次认证成功的时间戳
	 */
	private static long lastTimestamp = 0;

	public static void main(String[] args) {
		String password = "abcd12345";

		//时间戳密文有20个字符,0-19的映射关系(随机乱序)
		//加密、解密都需要的映射关系(发送和接收端需保持一致)
		int[] mapper = {15, 6, 17, 4, 10, 11, 13, 8, 19, 2, 0, 14, 12, 3, 9, 18, 16, 1, 5, 7};

		//将时间戳和密码混合在一起生成用于传输的密文
		String tokenVal = TokenUtil.encode(password, mapper);
		System.out.println(tokenVal);

		//接收方解析收到的密文,会得到时间戳和密码
		TokenUtil.Token token = TokenUtil.decode(tokenVal, mapper);
		long timestamp = token.getTimestamp();
		String pwd = token.getPassword();
		System.out.println(timestamp);
		System.out.println(pwd);

		if(timestamp>lastTimestamp && password.equals(pwd)){
			lastTimestamp = timestamp;
			System.out.println("认证通过...");
		}else{
			//即使tokenVal被截获也无法二次使用
			System.out.println("认证失败...");
		}
	}
}
  • 输出:
4YkWAJMjUZ2DTENy=MYzMQU1Mwj=NTz1
1603956561584
abcd12345
认证通过...
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值