google-idtoken验证

This commit is contained in:
2025-06-12 18:16:24 +08:00
committed by khalil
parent cb86e2da01
commit ce7b641e73
7 changed files with 187 additions and 25 deletions

View File

@@ -1,24 +1,19 @@
package com.accompany.admin.controller.api;
import cn.hutool.core.date.DateUtil;
import com.accompany.admin.service.api.MyApiService;
import com.accompany.business.message.UserEventBeginEndMessage;
import com.accompany.business.service.activity.h5.ActivityUserLevelExpService;
import com.accompany.business.service.mq.RocketMQService;
import com.accompany.business.service.room.RoomService;
import com.accompany.business.vo.RoomVo;
import com.accompany.common.netease.ErBanNetEaseService;
import com.accompany.common.netease.neteaseacc.result.RoomMemberRet;
import com.accompany.common.result.BusiResult;
import com.accompany.common.status.BusiStatus;
import com.accompany.common.utils.DateTimeUtil;
import com.accompany.core.exception.AdminServiceException;
import com.accompany.core.exception.ServiceException;
import com.accompany.core.service.GoogleTokenVerifier;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.List;
@RestController
@@ -313,4 +308,16 @@ public class MyApiController {
return BusiResult.success();
}
@Autowired
private GoogleTokenVerifier googleTokenVerifier;
@PostMapping("/auth/google")
public GoogleTokenVerifier.GoogleUserInfo authWithGoogle(String idToken) {
try {
return googleTokenVerifier.verifyAndGetUserInfo(idToken);
} catch (Exception e) {
throw new RuntimeException("Google token verification failed", e);
}
}
}

View File

@@ -29,6 +29,28 @@
<artifactId>accompany-sharding-service</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>2.7.0</version> <!-- 建议使用最新版本 -->
</dependency>
<!-- gRPC 核心库提供Context类 -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-core</artifactId>
<version>1.71.0</version> <!-- 使用最新稳定版 -->
</dependency>
<!-- OpenCensus解决trace相关错误 -->
<dependency>
<groupId>io.opencensus</groupId>
<artifactId>opencensus-api</artifactId>
<version>0.31.1</version>
</dependency>
<dependency>
<groupId>io.opencensus</groupId>
<artifactId>opencensus-impl</artifactId>
<version>0.31.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,90 @@
package com.accompany.core.service;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
@Slf4j
@Service
public class GoogleTokenVerifier {
private final GoogleIdTokenVerifier verifier;
private final String clientId;
public GoogleTokenVerifier(NetHttpTransport transport, GsonFactory jsonFactory, String clientId) {
this.clientId = clientId;
this.verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
.setAudience(Collections.singletonList(clientId))
.build();
}
/**
* 验证Token并返回用户信息
*/
public GoogleUserInfo verifyAndGetUserInfo(String idTokenString) {
try {
GoogleIdToken idToken = verifier.verify(idTokenString);
if (idToken == null) {
throw new IllegalArgumentException("Invalid ID token");
}
GoogleIdToken.Payload payload = idToken.getPayload();
return new GoogleUserInfo(
payload.getSubject(),
payload.getEmail(),
(String) payload.get("name"),
(String) payload.get("picture")
);
} catch (GeneralSecurityException e) {
log.error("verifyAndGetUserInfo:e:{}", e.getMessage(), e);
} catch (IOException e) {
log.error("verifyAndGetUserInfo:e:{}", e.getMessage(), e);
} catch (IllegalArgumentException e) {
log.error("verifyAndGetUserInfo:e:{}", e.getMessage(), e);
}
return null;
}
/**
* 仅获取用户邮箱
*/
public String getEmailFromToken(String idTokenString) {
return verifyAndGetUserInfo(idTokenString).getEmail();
}
/**
* 仅获取用户ID
*/
public String getUserIdFromToken(String idTokenString) {
return verifyAndGetUserInfo(idTokenString).getUserId();
}
/**
* 用户信息DTO
*/
public static class GoogleUserInfo {
private final String userId;
private final String email;
private final String name;
private final String pictureUrl;
public GoogleUserInfo(String userId, String email, String name, String pictureUrl) {
this.userId = userId;
this.email = email;
this.name = name;
this.pictureUrl = pictureUrl;
}
// Getters
public String getUserId() { return userId; }
public String getEmail() { return email; }
public String getName() { return name; }
public String getPictureUrl() { return pictureUrl; }
}
}

View File

@@ -0,0 +1,33 @@
package com.accompany.core.service.user;
import com.accompany.core.service.GoogleTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class GoogleAuthConfig {
@Value("${google-auth.client-id}")
private String clientId;
@Bean
public NetHttpTransport netHttpTransport() {
return new NetHttpTransport();
}
@Bean
public GsonFactory gsonFactory() {
return new GsonFactory();
}
@Bean
public GoogleTokenVerifier googleTokenVerifier(NetHttpTransport transport, GsonFactory jsonFactory) {
return new GoogleTokenVerifier(transport, jsonFactory, clientId);
}
}

View File

@@ -1,11 +1,12 @@
package com.accompany.core.service.user.impl;
import com.accompany.common.utils.GoogleJwtParser;
import com.accompany.common.utils.StringUtils;
import com.accompany.core.model.GoogleOpenidRef;
import com.accompany.core.mybatismapper.GoogleOpenidRefMapper;
import com.accompany.core.service.GoogleTokenVerifier;
import com.accompany.core.service.user.GoogleOpenidRefService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
@@ -21,20 +22,23 @@ import java.util.Optional;
public class GoogleOpenidRefServiceImpl extends ServiceImpl<GoogleOpenidRefMapper, GoogleOpenidRef> implements GoogleOpenidRefService {
@Autowired
private GoogleTokenVerifier googleTokenVerifier;
@Override
public String getUnionIdByEmail(String email, String idToken) {
GoogleOpenidRef googleOpenidRef = baseMapper.selectById(email);
return Optional.ofNullable(googleOpenidRef)
.map(x -> x.getOpenId())
.orElseGet(() -> {
String sub = GoogleJwtParser.getSubFromToken(idToken);
if (StringUtils.isNotEmpty(sub)) {
saveRecord(email, sub);
return sub;
GoogleTokenVerifier.GoogleUserInfo sub = googleTokenVerifier.verifyAndGetUserInfo(email);
if (sub != null && StringUtils.isNotEmpty(sub.getUserId()) && email.equals(sub.getEmail())) {
String userId = sub.getUserId();
saveRecord(email, userId);
return userId;
}
return null;
});
}
@Override

View File

@@ -1,23 +1,25 @@
package com.accompany.payment.google;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.Preconditions;
import com.google.api.client.util.Strings;
import com.google.api.services.androidpublisher.AndroidPublisher;
import com.google.api.services.androidpublisher.AndroidPublisherScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.util.Assert;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
@@ -33,9 +35,6 @@ public class AndroidPublisherHelper {
private static final Log log = LogFactory.getLog(AndroidPublisherHelper.class);
/** Global instance of the JSON factory. */
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
/** Global instance of the HTTP transport. */
private static HttpTransport HTTP_TRANSPORT;
@@ -45,6 +44,7 @@ public class AndroidPublisherHelper {
private static volatile String oldPackageName;
private static volatile String oldCredentialJson;
private static final GsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
public static void init(String applicationName, String packageName, String json) throws IOException, GeneralSecurityException {
if (needInit(applicationName, packageName, json)) {
@@ -56,14 +56,20 @@ public class AndroidPublisherHelper {
List<String> scopes = new ArrayList<>();
scopes.add(AndroidPublisherScopes.ANDROIDPUBLISHER);
ByteArrayResource resource = new ByteArrayResource(json.getBytes());
GoogleCredential credential = GoogleCredential.fromStream(resource.getInputStream())
.createScoped(scopes);
// 1. 使用HttpCredentialsAdapter包装GoogleCredentials
GoogleCredentials credentials = GoogleCredentials.fromStream(
new ByteArrayInputStream(json.getBytes()))
.createScoped(Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER));
HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);
newTrustedTransport();
//使用谷歌凭据和收据从谷歌获取购买信息
androidPublisher = new AndroidPublisher.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
// 2. 构建AndroidPublisher实例
androidPublisher = new AndroidPublisher.Builder(
HTTP_TRANSPORT,
JSON_FACTORY,
requestInitializer)
.setApplicationName(applicationName)
.build();

View File

@@ -69,7 +69,7 @@
<opencc4j.version>1.7.2</opencc4j.version>
<hutool.version>5.8.38</hutool.version>
<hippo4j-config-spring-boot-starter.version>1.5.0</hippo4j-config-spring-boot-starter.version>
<google-api-services-androidpublisher.version>v3-rev24-1.24.1</google-api-services-androidpublisher.version>
<google-api-services-androidpublisher.version>v3-rev20231115-2.0.0</google-api-services-androidpublisher.version>
<payermax-server.version>1.0.12</payermax-server.version>
<sharding-jdbc.version>5.5.2</sharding-jdbc.version>
<commons-lang.version>2.6</commons-lang.version>