Flutter通过serverAuthCode在Spring后端获取OAuth2.0 Access Token的问题
Google OAuth 登录流程问题解答
问题1:流程是否正确?正确流程是什么?
这个流程是完全正确且推荐的。
直接从Flutter前端向Spring后端发送Access Token确实存在安全隐患:Access Token是用户身份凭证,传输过程中若被拦截,攻击者可直接用它调用Google API或你的业务接口;且Access Token虽有效期短,但泄露后的风险不可忽视。
而serverAuthCode是一次性授权码,仅能用于兑换一次Access Token,即便传输中被拦截,攻击者也无法重复使用,安全性更高。
标准安全流程如下:
- Flutter端通过
google_sign_in插件引导用户完成Google授权,获取serverAuthCode - Flutter将
serverAuthCode发送至你的Spring后端 - Spring后端使用
serverAuthCode,结合Google Cloud Console中注册的客户端ID、客户端密钥,向Google OAuth服务器请求兑换Access Token和Refresh Token - 后端安全存储Refresh Token(用于后续自动刷新Access Token),生成自身业务令牌(如JWT)返回给Flutter前端,前端后续用该业务令牌访问后端接口
问题2:Spring服务器中通过serverAuthCode获取Access Token的实现方式
方式1:使用Spring Security OAuth2 Client(推荐)
若项目已集成Spring Security,这是最简洁的实现方式:
- 在
application.yml中配置Google OAuth2客户端信息:
spring: security: oauth2: client: registration: google: client-id: 你的Google客户端ID client-secret: 你的Google客户端密钥 scope: openid,email,profile # 根据业务需求配置权限范围 provider: google: token-uri: https://oauth2.googleapis.com/token
- 业务代码中注入相关组件完成令牌兑换:
import org.springframework.security.oauth2.client.endpoint.AuthorizationCodeTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; import org.springframework.stereotype.Service; @Service public class GoogleOAuthService { private final ClientRegistrationRepository clientRegistrationRepository; private final AuthorizationCodeTokenResponseClient tokenResponseClient; public GoogleOAuthService(ClientRegistrationRepository clientRegistrationRepository, AuthorizationCodeTokenResponseClient tokenResponseClient) { this.clientRegistrationRepository = clientRegistrationRepository; this.tokenResponseClient = tokenResponseClient; } public String getAccessToken(String serverAuthCode) { // 获取Google客户端注册信息 ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId("google"); // 构建包含serverAuthCode的授权响应 OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponse.success(serverAuthCode) .redirectUri(clientRegistration.getRedirectUri()) .build(); // 构建授权请求 OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode() .clientId(clientRegistration.getClientId()) .redirectUri(clientRegistration.getRedirectUri()) .scopes(clientRegistration.getScopes()) .build(); OAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse); OAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration, authorizationExchange); // 向Google请求兑换令牌并返回Access Token return tokenResponseClient.getTokenResponse(grantRequest).getAccessToken().getTokenValue(); } }
方式2:直接使用RestTemplate发送请求
若不想依赖Spring Security OAuth2组件,可直接调用Google令牌接口:
import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import java.util.Map; public class GoogleOAuthUtil { private static final String GOOGLE_TOKEN_URI = "https://oauth2.googleapis.com/token"; private static final String CLIENT_ID = "你的Google客户端ID"; private static final String CLIENT_SECRET = "你的Google客户端密钥"; // 注意:此处redirectUri必须与Flutter端获取serverAuthCode时配置的地址一致 private static final String REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob"; // 或你在Google控制台配置的回调地址 public static String getAccessToken(String serverAuthCode) { RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.add("grant_type", "authorization_code"); params.add("code", serverAuthCode); params.add("client_id", CLIENT_ID); params.add("client_secret", CLIENT_SECRET); params.add("redirect_uri", REDIRECT_URI); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers); Map<String, Object> response = restTemplate.postForObject(GOOGLE_TOKEN_URI, request, Map.class); return (String) response.get("access_token"); } }
注意事项
- 确保Google Cloud Console中,OAuth 2.0客户端ID已正确配置授权范围和回调地址
- 客户端密钥需严格保密,绝对不能暴露在前端代码中
- 兑换成功后,Google会返回
access_token、refresh_token、expires_in等信息,建议后端存储refresh_token,用于Access Token过期时自动刷新
内容的提问来源于stack exchange,提问作者노제원
相关产品推荐
相关产品推荐

