From 44093d1a4b9391810402ad959bd0c3e4cf8d8da6 Mon Sep 17 00:00:00 2001 From: tim Date: Wed, 16 Jul 2025 18:51:54 +0800 Subject: [PATCH] feat: update X OAuth --- .../openisle/service/TwitterAuthService.java | 96 +++++++++++-------- 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/openisle/service/TwitterAuthService.java b/src/main/java/com/openisle/service/TwitterAuthService.java index bc24bf96a..9a2748610 100644 --- a/src/main/java/com/openisle/service/TwitterAuthService.java +++ b/src/main/java/com/openisle/service/TwitterAuthService.java @@ -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 authenticate(String code, String codeVerifier, com.openisle.model.RegisterMode mode, String redirectUri) { + public Optional 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 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 tokenRes; try { - String tokenUrl = "https://api.twitter.com/2/oauth2/token"; - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - - MultiValueMap 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> request = new HttpEntity<>(body, headers); - ResponseEntity 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 entity = new HttpEntity<>(authHeaders); - ResponseEntity 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 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) {