[網站開發]總結 Google OAuth 2.0 認證登入與註冊的實作

Google OAuth 2.0 是一種安全協議,用於向第三方應用程序授予訪問用戶資源的權限,而無需向第三方應用程序透露用戶的憑據。以下是實現 Google OAuth 2.0 登入與註冊的步驟:

  1. 在 Google Cloud Platform (GCP)上註冊 Oauth 2.0 應用
    • 建立專案(如果還沒有專案的話)
    • 進入專案
    • 選擇 API和服務
    • 選擇 憑證
    • 建立憑證,選擇 OAuth用戶端ID
    • 應用程式類型選擇網頁應用程式,名稱隨便打方便確認就好,然後填寫已授權的 JavaScript 來源已授權的重新導向 URI,例如說你如果在local上開發,那已授權的 JavaScript 來源可以寫http://localhost:9000(port自己調整),已授權的重新導向 URI可以寫http://localhost:9000/GoogleAuthRedirect(反正就看你要重新導向到哪個path),好了之後建立
    • 把該憑證的client_id跟client_secret存起來
  2. 前端實現 Google OAuth 2.0 登入
    • 發起 Google 登入請求,當用戶點擊「使用 Google 登入」按鈕時,將用戶重定向到 Google 的授權頁面。
  3. 捕獲授權碼並發送到後端,在重定向 URI 頁面上捕獲授權碼(authorizationCode),並將其發送到後端進行處理。
  4. 後端處理授權碼
    • 用授權碼向google取得access token
  5. 更新DB

以下代碼使用的僅供參考,環境是使用vue3+ springbott + java17,以及實作過程中要注意的點:

1. 授權碼無法正確交換令牌
確認 Google Cloud Console 中配置的重定向 URI 是否與應用程序中的一致,後端的 redirect_uri 值需要設定成已授權的重新導向 URI的值
確認 client_id 和 client_secret 是否正確

2. 用戶已存在的處理

3. 確認access token,傳遞時使用https,憑證資訊千萬別hard code

//前端實現 Google OAuth 2.0 登入
const loginWithGoogle = async () => {
  const GOOGLE_CLIENT_ID = 'YOUR_GOOGLE_CLIENT_ID';
  const GOOGLE_REDIRECT_URI = 'http://localhost:9000/GoogleAuthRedirect';
  window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${GOOGLE_CLIENT_ID}&redirect_uri=${GOOGLE_REDIRECT_URI}&response_type=code&scope=openid%20email%20profile&access_type=offline&prompt=consent`;
};
// 在重定向 URI 的 JavaScript 文件中
const getAuthorizationCode = () => {
  const urlParams = new URLSearchParams(window.location.search);
  return urlParams.get('code');
};

const sendAuthorizationCode = async (authorizationCode) => {
  try {
//請自行定義要打到後端的path
    const response = await axios.post('/api/auth/google-auth', { code: authorizationCode });
    console.log('Authorization successful:', response.data);
  } catch (error) {
    console.error('Authorization failed:', error);
  }
};

const authorizationCode = getAuthorizationCode();
if (authorizationCode) {
  sendAuthorizationCode(authorizationCode);
}
//後端處理授權碼
@RestController
@RequestMapping("/api/auth")
public class GoogleAuthController {

    @Value("${google.client.id}")
    private String clientId;

    @Value("${google.client.secret}")
    private String clientSecret;

    @Autowired
    private GoogleAuthService googleAuthService;

    @PostMapping("/google-auth")
    public ResponseEntity<?> handleAuthorizationCode(@RequestBody AuthorizationCodeRequest request) {
        String authorizationCode = request.getCode();

        String tokenUrl = "https://oauth2.googleapis.com/token";
        RestTemplate restTemplate = new RestTemplate();
        Map<String, String> requestBody = new HashMap<>();
        requestBody.put("code", authorizationCode);
        requestBody.put("client_id", clientId);
        requestBody.put("client_secret", clientSecret);
        requestBody.put("redirect_uri", "http://localhost:9000/GoogleAuthRedirect");
        requestBody.put("grant_type", "authorization_code");

        try {
            ResponseEntity<Map> responseEntity = restTemplate.postForEntity(tokenUrl, requestBody, Map.class);

            Map<String, Object> responseBody = responseEntity.getBody();
            String accessToken = (String) responseBody.get("access_token");

            String userInfoUrl = "https://www.googleapis.com/oauth2/v1/userinfo?access_token=" + accessToken;
            ResponseEntity<Map> userInfoResponse = restTemplate.getForEntity(userInfoUrl, Map.class);
            Map<String, Object> userInfo = userInfoResponse.getBody();

            String email = (String) userInfo.get("email");
            Boolean isSuccess = googleAuthService.registerGoogleUser(email);

            return ResponseEntity.ok(userInfo);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to authenticate with Google");
        }
    }

    static class AuthorizationCodeRequest {
        private String code;
        public String getCode() { return code; }
        public void setCode(String code) { this.code = code; }
    }
}
//更新DB
@Service
public class GoogleAuthService {

    @Autowired
    private UserRepository userRepository;

    public Boolean registerGoogleUser(String email) {
        if (userRepository.existsByEmail(email)) {
            return false;
        }

        User user = new User();
        user.setEmail(email);
        userRepository.save(user);
        return true;
    }
}

發佈留言