我们在开发中需要用到对接口的登录鉴权,
首先后台会根据几个参数组成字符串+secretKey,用AES(ECB)加密算法来生成秘钥ticket,第三方访问的时候,带在提供的系统认证地址后面(列如http(s)://test.com?ticket=秘钥)
参数:这些参数包括时间戳,姓名,手机号,8位随机数,这些都是基本的,有的还需要根据实际业务场景添加一些信息,比如人员所在地,所在行政区划,年龄,职位信息等等。
字符串参数生成规则:
timeStamp=时间戳&mobile=89757&userName=柳如是&random=(8位随机数)
前端:获取应用认证地址后面的ticket参数,传给自己应用的后端去处理,处理成功则跳转,否则报错
后端:获取前端传过来的tcket,用secretKey来解密,获取相关的参数信息,如时间戳timeStamp可用来判断是否超时(可自行设置超时时间,超时可报错),手机号mobile可用来查询自己系统的用户,生成token等,验证成功后返回给前端,前端跳转到相应系统页面。
根据以上需求:我们来用代码实现一下:
(1)首先根据几个参数组成字符串+secretKey,用AES(ECB)加密算法来生成秘钥ticket
/**
* AES加密解密工具类
* @Author: zigao
* @Date: 2021/6/6 9:27
*/
public class AesUtils {
/**
* aes解密
* @param cliperText
* @param secretKey
* @return
* @throws Exception
*/
public static String aesDecrypt(String cliperText, String secretKey) throws Exception {
byte[] raw = secretKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
//Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
// 先用base64解码
byte[] encrypted1 = Base64.getDecoder().decode(cliperText);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, "UTF-8");
return originalString;
}
/**
* aes加密
* @param plainText
* @param secretKey
* @return
* @throws Exception
*/
public static String aesEncrypt(String plainText, String secretKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
byte[] raw = secretKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
Base64.Encoder encoder = Base64.getEncoder();
// 此处使用BASE64做转码
return encoder.encodeToString(encrypted);
}
}
/**
* Ticket工具类
* @Author: zigao
* @Date: 2021/6/7 10:28
*/
public class TicketUtils {
/**
* 加盐(SECRET)
*/
public static final String SECRET_KEY = "1dvFasdTGB&sdsd@";
/**
* 生成ticket
* @param timeStamp
* @param mobile
* @param userName
* @param random
* @return
* @throws Exception
*/
public static String genernateTicket(long timeStamp, String mobile, String userName, String random) throws Exception {
//生成字符串规则
String content = "timeStamp=" + timeStamp + "&mobile=" + mobile + "&userName=" + userName + "&random=" + random;
//字符串+secret_key进行AES加密
String ticket = AesUtils.aesEncrypt(content,SECRET_KEY);
return ticket;
}
}
我们来测试下:
/**
* 获取ticket
*
* @param
* @return
*/
@PostMapping("/getTicket")
public ResponseMessage getTicket() {
ResponseMessage responseMessage = new ResponseMessage(0);
try {
//生成时戳
long timeStamp = System.currentTimeMillis();
//手机号
String mobile = "89757";
//用户名
String userName = "柳如是";
//随机数(8位,我这里做测试,正式开发建议用随机数工具类)
String random = "wsxc45f1";
String ticket = TicketUtils.genernateTicket(timeStamp, mobile, userName, random);
responseMessage.setMessage(ticket);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return responseMessage;
}
ticket生成成功。
(2)获取前端传过来的tcket,用secretKey来解密,获取相关的参数信息,生成token
/**
* 登录解析工具类
* @Author: zigao
* @Date: 2021/6/7 11:07
*/
public class SdkUtils {
private final static Logger logger = LoggerFactory.getLogger(SdkUtils.class);
/**
* 加盐(SECRET)
*/
public static final String SECRET_KEY = "1dvFasdTGB&sdsd@";
/**
* 解析
* @param ticket
* @param
* @return
* @throws Exception
*/
public static Map validateTicket(String ticket) throws Exception {
//进行解密+secretKey
String resultJson = AesUtils.aesDecrypt(ticket, SECRET_KEY);
logger.info("resultJson解密输出==>" + resultJson);
//字符串转成map
Map map = SdkUtils.getMap(resultJson);
return map;
}
/**
* 字符串转map集合
* @param strValue
* @return
*/
public static Map getMap(String strValue){
Map map = new HashMap();
String[] text = strValue.split("&"); // 转换为数组
for (String str : text) {
String[] keyText = str.split("="); // 转换key与value的数组
if (keyText.length < 1) {
continue;
}
String key = keyText[0]; // key
String value = keyText[1]; // value
map.put(key, value);
}
return map;
}
}
/**
* token信息
* @Author: zigao
* @Date: 2021/6/7 13:44
*/
@Data
public class UserToken implements Serializable {
//token 会话令牌
String token;
//用户编号
String mobile;
//用户名
String userName;
}
/**
* 获取token
*
* @param
* @return
*/
@PostMapping("/getToken")
public ResponseMessage getToken(@RequestBody Map map) {
ResponseMessage responseMessage = new ResponseMessage(0);
try {
//获取ticket
String ticket = (String) map.get("ticket");
Map mapValue = SdkUtils.validateTicket(ticket);
UserToken userToken = new UserToken();
userToken.setToken(UUID.randomUUID().toString());
userToken.setUserName(mapValue.get("userName").toString());
userToken.setMobile(mapValue.get("mobile").toString());
responseMessage.setMessage(userToken);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return responseMessage;
}