feat: update X OAuth

This commit is contained in:
tim
2025-07-16 18:51:54 +08:00
parent efe5a51889
commit 44093d1a4b

View File

@@ -8,6 +8,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.util.MultiValueMap;
import org.springframework.util.LinkedMultiValueMap;
@@ -23,49 +24,64 @@ public class TwitterAuthService {
@Value("${twitter.client-id:}")
private String clientId;
public Optional<User> authenticate(String code, String codeVerifier, com.openisle.model.RegisterMode mode, String redirectUri) {
public Optional<User> authenticate(
String code,
String codeVerifier,
RegisterMode mode,
String redirectUri) {
// 1. 交换 token
String tokenUrl = "https://api.twitter.com/2/oauth2/token";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
// Twitter PKCE 要求的五个参数
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("client_id", clientId);
body.add("grant_type", "authorization_code");
body.add("code", code);
body.add("code_verifier", codeVerifier);
body.add("redirect_uri", redirectUri); // 一律必填
// 如果你的 app 属于机密客户端,必须带 client_secret
// body.add("client_secret", clientSecret);
ResponseEntity<JsonNode> tokenRes;
try {
String tokenUrl = "https://api.twitter.com/2/oauth2/token";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("client_id", clientId);
body.add("code", code);
body.add("grant_type", "authorization_code");
body.add("code_verifier", codeVerifier);
if (redirectUri != null) {
body.add("redirect_uri", redirectUri);
}
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
ResponseEntity<JsonNode> tokenRes = restTemplate.postForEntity(tokenUrl, request, JsonNode.class);
if (!tokenRes.getStatusCode().is2xxSuccessful() || tokenRes.getBody() == null || !tokenRes.getBody().has("access_token")) {
return Optional.empty();
}
String accessToken = tokenRes.getBody().get("access_token").asText();
HttpHeaders authHeaders = new HttpHeaders();
authHeaders.setBearerAuth(accessToken);
HttpEntity<Void> entity = new HttpEntity<>(authHeaders);
ResponseEntity<JsonNode> userRes = restTemplate.exchange(
"https://api.twitter.com/2/users/me", HttpMethod.GET, entity, JsonNode.class);
if (!userRes.getStatusCode().is2xxSuccessful() || userRes.getBody() == null) {
return Optional.empty();
}
JsonNode userNode = userRes.getBody();
String username = userNode.hasNonNull("username") ? userNode.get("username").asText() : null;
String email = null;
if (userNode.hasNonNull("email")) {
email = userNode.get("email").asText();
}
if (email == null) {
email = username + "@twitter.com";
}
return Optional.of(processUser(email, username, mode));
} catch (Exception e) {
tokenRes = restTemplate.postForEntity(tokenUrl, new HttpEntity<>(body, headers), JsonNode.class);
} catch (HttpClientErrorException e) {
return Optional.empty();
}
JsonNode tokenJson = tokenRes.getBody();
if (tokenJson == null || !tokenJson.hasNonNull("access_token")) {
return Optional.empty();
}
String accessToken = tokenJson.get("access_token").asText();
// 2. 拉取用户信息
HttpHeaders authHeaders = new HttpHeaders();
authHeaders.setBearerAuth(accessToken);
ResponseEntity<JsonNode> userRes;
try {
userRes = restTemplate.exchange(
"https://api.twitter.com/2/users/me",
HttpMethod.GET,
new HttpEntity<>(authHeaders),
JsonNode.class);
} catch (HttpClientErrorException e) {
return Optional.empty();
}
JsonNode data = userRes.getBody() == null ? null : userRes.getBody().path("data");
String username = data != null ? data.path("username").asText(null) : null;
if (username == null) {
return Optional.empty();
}
// Twitter v2 默认拿不到 email如果你申请到 email.scope可改用 /2/users/:id?user.fields=email
String email = username + "@twitter.com";
return Optional.of(processUser(email, username, mode));
}
private User processUser(String email, String username, com.openisle.model.RegisterMode mode) {