From 34f666719adc6785c7fd86c56c02e347c19412d2 Mon Sep 17 00:00:00 2001 From: xupei Date: Thu, 6 Nov 2025 14:36:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=8D=A2=E8=B0=B7=E6=AD=8C=E7=99=BB?= =?UTF-8?q?=E5=BD=95token=E8=A7=A3=E6=9E=90=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aida/lanecarford/config/WebConfig.java | 2 +- .../controller/LoginController.java | 13 +++- .../lanecarford/service/LoginService.java | 2 + .../service/impl/LoginServiceImpl.java | 73 ++++++++++++++++++- src/main/resources/application-dev.yml | 3 +- src/main/resources/application-prod.yml | 3 +- 6 files changed, 90 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/aida/lanecarford/config/WebConfig.java b/src/main/java/com/aida/lanecarford/config/WebConfig.java index a3e3d73..bfa9505 100644 --- a/src/main/java/com/aida/lanecarford/config/WebConfig.java +++ b/src/main/java/com/aida/lanecarford/config/WebConfig.java @@ -40,7 +40,7 @@ public class WebConfig implements WebMvcConfigurer { registry.addInterceptor(jwtInterceptor) .addPathPatterns("/api/**/**") // 保护这些路径 .excludePathPatterns(Arrays.asList("/api/auth/precheckEmail", "/api/auth/registerOrLogin", - "/api/auth/forgotPwd", "/api/style/callback", "/api/auth/parseGoogleCredential")); // 排除登录接口 + "/api/auth/forgotPwd", "/api/style/callback", "/api/auth/parseGoogleAccessToken")); // 排除登录接口 } /** diff --git a/src/main/java/com/aida/lanecarford/controller/LoginController.java b/src/main/java/com/aida/lanecarford/controller/LoginController.java index 3b06cba..6f5251d 100644 --- a/src/main/java/com/aida/lanecarford/controller/LoginController.java +++ b/src/main/java/com/aida/lanecarford/controller/LoginController.java @@ -106,10 +106,19 @@ public class LoginController { * 前端直接将credential传给服务端验证 * 服务端直接解析和验证JWT,无需与Google服务器交换 */ - @CrossOrigin - @GetMapping("/parseGoogleCredential") +// @GetMapping("/parseGoogleCredential") public ApiResponse parseGoogleCredential(@RequestParam("credential") String credential) throws ParseException, IOException, JOSEException { return ApiResponse.success(loginService.parseGoogleCredential(credential)); } + /** + * 使用OAuth 2.0方式登录 + * @param accessToken 前端通过Google回调获取的token + * @return 用户登录信息 + */ + @GetMapping("/parseGoogleAccessToken") + public ApiResponse parseGoogleAccessToken(@RequestParam("accessToken") String accessToken) { + return ApiResponse.success(loginService.parseGoogleAccessToken(accessToken)); + } + } diff --git a/src/main/java/com/aida/lanecarford/service/LoginService.java b/src/main/java/com/aida/lanecarford/service/LoginService.java index 8c0e07a..e391aa8 100644 --- a/src/main/java/com/aida/lanecarford/service/LoginService.java +++ b/src/main/java/com/aida/lanecarford/service/LoginService.java @@ -34,4 +34,6 @@ public interface LoginService extends IService { User getUserInfo(); LoginVO parseGoogleCredential(String credential) throws ParseException, JOSEException, IOException; + + LoginVO parseGoogleAccessToken(String accessToken); } diff --git a/src/main/java/com/aida/lanecarford/service/impl/LoginServiceImpl.java b/src/main/java/com/aida/lanecarford/service/impl/LoginServiceImpl.java index 7c2a055..38719ec 100644 --- a/src/main/java/com/aida/lanecarford/service/impl/LoginServiceImpl.java +++ b/src/main/java/com/aida/lanecarford/service/impl/LoginServiceImpl.java @@ -6,6 +6,7 @@ import com.aida.lanecarford.common.enums.LanguageEnum; import com.aida.lanecarford.common.response.ResultEnum; import com.aida.lanecarford.common.security.JwtUtil; import com.aida.lanecarford.common.security.context.UserContext; +import com.aida.lanecarford.dto.GoogleUser; import com.aida.lanecarford.dto.LoginRequest; import com.aida.lanecarford.entity.User; import com.aida.lanecarford.exception.BusinessException; @@ -16,9 +17,11 @@ import com.aida.lanecarford.util.RandomsUtil; import com.aida.lanecarford.util.SendEmailUtil; import com.aida.lanecarford.vo.AuthPrincipalVO; import com.aida.lanecarford.vo.LoginVO; +import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.jwk.JWK; @@ -30,12 +33,13 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; import java.io.IOException; import java.net.URL; import java.text.ParseException; import java.time.LocalDateTime; -import java.util.Objects; +import java.util.*; import static com.aida.lanecarford.common.enums.AuthenticationOperationTypeEnum.*; @@ -58,6 +62,8 @@ public class LoginServiceImpl extends ServiceImpl implements L private String googleClientId; @Value("${google.client_secret}") private String googleClientSecret; + @Value("${google.redirect_uri}") + private String googleRedirectUri; @Override @@ -316,7 +322,72 @@ public class LoginServiceImpl extends ServiceImpl implements L }; } + @Override + public LoginVO parseGoogleAccessToken(String accessToken) { + try { + log.info("accessToken: {}", accessToken); + // 使用 accessToken 获取 Google 用户信息 +// GoogleUser googleUser = getGoogleUserFromCode(accessToken); + GoogleUser googleUser = getGoogleUserInfo(accessToken); + log.info("googleUser: {}", JSON.toJSONString(googleUser)); + // 1. 根据用户邮箱查询是登录还是注册 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.lambda().eq(User::getEmail, googleUser.getEmail()); + + User user = getOne(queryWrapper); + AuthenticationOperationTypeEnum type = Objects.isNull(user) ? REGISTER: LOGIN; + LoginRequest loginRequest = new LoginRequest(); + loginRequest.setName(googleUser.getName()); + loginRequest.setEmail(googleUser.getEmail()); + loginRequest.setOperationType(type.name()); + + return switch (type) { + case REGISTER -> register(loginRequest); + case LOGIN -> login(loginRequest); + default -> throw new BusinessException("Unknown authentication operation type."); + }; + } catch (Exception e) { + throw new BusinessException("Error processing Google login: " + e.getMessage()); + } + } + + private static final String TOKEN_URL = "https://oauth2.googleapis.com/token"; + + public GoogleUser getGoogleUserFromCode(String code) { + // Step 1: Exchange code for access_token + String accessToken = getAccessToken(code); + log.info("accessToken" + accessToken); + + // Step 2: Use access_token to get Google User info + return getGoogleUserInfo(accessToken); + } + + private String getAccessToken(String code) { + RestTemplate restTemplate = new RestTemplate(); + Map params = new HashMap<>(); + params.put("client_id", googleClientId); + params.put("client_secret", googleClientSecret); + params.put("redirect_uri", googleRedirectUri); + params.put("grant_type", "authorization_code"); + params.put("code", code); + + // 使用 RestTemplate 发起请求以获取 access_token + GoogleTokenResponse response = restTemplate.postForObject(TOKEN_URL, params, GoogleTokenResponse.class); + return response.getAccessToken(); + } + + private static final String USER_INFO_URL = "https://www.googleapis.com/oauth2/v3/userinfo"; + private GoogleUser getGoogleUserInfo(String accessToken) { + /*System.setProperty("http.proxyHost", "127.0.0.1"); + System.setProperty("http.proxyPort", "7890"); + System.setProperty("https.proxyHost", "127.0.0.1"); + System.setProperty("https.proxyPort", "7890");*/ + RestTemplate restTemplate = new RestTemplate(); + String url = USER_INFO_URL + "?access_token=" + accessToken; + return restTemplate.getForObject(url, GoogleUser.class); + } + } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 667febf..2e00e09 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -91,4 +91,5 @@ webhook: google: client_id: 216037134725-7q8vqp0ohtmohlosltkfg7bd2v29rm5a.apps.googleusercontent.com - client_secret: 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 \ No newline at end of file + client_secret: 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 + redirect_uri: ${webhook.domain}/api/auth/google_callback \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 358840e..312b0b9 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -91,4 +91,5 @@ webhook: google: client_id: 216037134725-7q8vqp0ohtmohlosltkfg7bd2v29rm5a.apps.googleusercontent.com - client_secret: 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 \ No newline at end of file + client_secret: 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 + redirect_uri: ${webhook.domain}/api/auth/google_callback \ No newline at end of file