[英]Login with Telegram with Spring Security
我正在尝试在我的 Spring Boot 应用程序上使用 Telegram ( https://core.telegram.org/widgets/login ) 实现登录,但遇到了一个问题。
我一直在尝试实现他们提供的 PHP 代码来验证身份验证,但是出了点问题,我不明白是什么。
所以,这就是 PHP 上的代码
secret_key = SHA256(<bot_token>)
if (hex(HMAC_SHA256(data_check_string, secret_key)) == hash) {
// data is from Telegram
}
数据检查字符串是所有接收到的字段的串联,按字母顺序排序,格式为key=<value>
,换行符 ('\\n', 0xA0) 用作分隔符 - 例如, 'auth_date=<auth_date>\\nfirst_name=<first_name>\\nid=<id>\\nusername=<username>
。
所以,我所做的是:
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class AuthenticationRequest {
@NotNull
private Long authDate;
private String firstName;
@NotEmpty
private String id;
private String lastName;
private String photoUrl;
private String username;
@NotEmpty
private String hash;
@Override
public String toString() {
final var data = new StringBuilder();
for (final Field field : getClass().getDeclaredFields()) {
try {
if (!field.getName().equals("hash") && field.get(this) != null) {
final var fieldName = CaseFormat.LOWER_CAMEL
.to(CaseFormat.LOWER_UNDERSCORE, field.getName());
data.append(fieldName).append("=").append(field.get(this)).append("\\n");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return data.substring(0, data.length() - 2);
}
}
以及这两种方法:
private static String hmacSha256(final String data, final byte[] secret) {
try {
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(secret, "HmacSHA256");
sha256Hmac.init(secretKey);
byte[] signedBytes = sha256Hmac.doFinal(data.getBytes());
return bytesToHex(signedBytes);
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
return null;
}
}
private static String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder();
for (final byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
当我比较它们时,它们是完全不同的两个字符串
final var telegramData = authenticationRequest.toString();
final var digest = MessageDigest.getInstance("SHA-256");
final var hashedToken = digest.digest(botToken.getBytes());
System.out.println(authenticationRequest.getHash());
System.out.println(hmacSha256(telegramData, hashedToken));
你能告诉我我做错了什么吗? 也许我完全误解了我必须验证身份验证数据的方式,或者我错过了什么?
试试这个,代码有点丑,但效果很好!
public boolean verifyAuth(JsonObject Telegram_User){
String hash = Telegram_User.remove("hash").getAsString();
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
String[] t = Telegram_User.toString().replace("{","").replace("}","").replace("\":","=").replace(",","\n").replace("\"","").split("\n");
sha256_HMAC.init(new SecretKeySpec(MessageDigest.getInstance("SHA-256").digest(BezouroBot.telegram.getBotToken().getBytes(StandardCharsets.UTF_8)),"SHA256"));
Arrays.sort(t);
StringBuilder i = new StringBuilder();
boolean First = true;
for (String s : t) if(First){ First = false; i = new StringBuilder(s);} else i.append("\n").append(s);
return Hex.encodeHexString(sha256_HMAC.doFinal(i.toString().getBytes())).equals(hash);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
return false;
}
}
这是我的工具
// define your token to a variable
private final String TELEGRAM_TOKEN = ""
@PostMapping("auth/telegram")
public ResponseEntity<Object> telegramAuth(@RequestBody Map<String, Object> request) {
String hash = (String) request.get("hash");
request.remove("hash");
// Prepare the string
String str = request.entrySet().stream()
.sorted((a, b) -> a.getKey().compareToIgnoreCase(b.getKey()))
.map(kvp -> kvp.getKey() + "=" + kvp.getValue())
.collect(Collectors.joining("\n"));
try {
SecretKeySpec sk = new SecretKeySpec(
// Get SHA 256 from telegram token
MessageDigest.getInstance("SHA-256").digest(TELEGRAM_TOKEN.getBytes(StandardCharsets.UTF_8)
), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(sk);
byte[] result = mac.doFinal(str.getBytes(StandardCharsets.UTF_8));
// Convert the result to hex string
// Like https://stackoverflow.com/questions/9655181
String resultStr = ByteBufUtil.bytesToHex(result);
// Compare the result with the hash from body
if(hash.compareToIgnoreCase(resultStr) == 0) {
// Do other things like create a user and JWT token
return ResponseEntity.ok("ok");
} else {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(
new MessageResponse("Login info hash mismatch")
);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(
new MessageResponse("Server error while authenticating")
);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.