資安等級:高

綁定學校 Google 帳號
免密碼自動查詢

利用學校網域 (Workspace) 的驗證機制,學生只需登入 Google 帳號,系統自動辨識身分並顯示成績。無需管理密碼,杜絕代查。

運作原理比較

一般版
輸入學號+密碼 比對資料庫
本方案
Secure
自動抓取 Email Session.getActiveUser()
直接顯示成績

Workspace 版建置四步驟

主要的差異在於「資料庫欄位」與「後端程式碼」。請確保您與學生都在同一個 Google 網域下 (例如都在 @gapp.ylsh.ilc.edu.tw)。

修改試算表欄位

將原本的「密碼」欄位改為 Email。系統將以此作為唯一的驗證依據。

小技巧: 請將學生的 Email 完整填入,例如 s410001@gapp.ylsh.ilc.edu.tw
成績單 (工作表名稱)
A (Email) *關鍵 B (姓名) C (國文) D (數學)
s410001@gapp.ylsh.ilc.edu.tw 王小明 90 85
s410002@gapp.ylsh.ilc.edu.tw 李小華 88 92

後端程式碼 (程式碼.gs)

我們使用 Session.getActiveUser().getEmail() 來自動抓取使用者的 Email。不需要傳入任何參數。

function doGet() {
  return HtmlService.createTemplateFromFile('Index').evaluate()
      .setTitle('我的成績單')
      .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
      .addMetaTag('viewport', 'width=device-width, initial-scale=1');
}

// 注意:這裡不需要傳入學號密碼,直接從 Session 抓
function getMyGrade() {
  // 自動取得目前開啟網頁的使用者 Email
  var userEmail = Session.getActiveUser().getEmail();
  
  // 如果抓不到 Email (例如權限設定錯誤),回傳錯誤
  if (!userEmail) {
    return { status: "error", message: "無法識別您的身分,請確認您已登入學校帳號。" };
  }

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName("成績單");
  var data = sheet.getDataRange().getDisplayValues();
  
  // 開始比對 Email (假設 Email 在第一欄 A欄)
  for (var i = 1; i < data.length; i++) {
    // 忽略大小寫差異
    if (data[i][0].toString().trim().toLowerCase() == userEmail.toLowerCase()) {
      return {
        status: "success",
        email: userEmail,
        header: data[0],
        result: data[i]
      };
    }
  }
  
  return { status: "failed", email: userEmail };
}

前端介面 (Index.html)

介面變得更簡潔了!不需要輸入框,只有一個「載入中」的畫面,網頁開啟後會自動執行查詢。

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <meta charset="UTF-8">
    <style>
      body { font-family: "Microsoft JhengHei", sans-serif; padding: 20px; background-color: #f0f2f5; display: flex; justify-content: center; }
      .container { width: 100%; max-width: 500px; background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); }
      h2 { text-align: center; color: #1a73e8; margin-bottom: 20px; }
      .loading { text-align: center; padding: 20px; color: #666; }
      .profile { text-align: center; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid #eee; }
      .email-tag { background: #e8f0fe; color: #1a73e8; padding: 4px 12px; border-radius: 20px; font-size: 0.9em; }
      .item { display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px dashed #eee; }
      .score { font-weight: bold; color: #333; }
      .error { color: #d93025; text-align: center; padding: 20px; background: #fce8e6; border-radius: 8px; display: none; }
    </style>
  </head>
  <body>
    <div class="container">
      <h2>🏫 學期成績單</h2>
      
      <!-- 載入中狀態 -->
      <div id="loading" class="loading">
        正在確認您的學校帳號身分...<br>請稍候
      </div>

      <div id="errorMsg" class="error"></div>

      <div id="resultArea" style="display:none;">
        <div class="profile">
          <span id="userEmail" class="email-tag"></span>
          <div style="margin-top:10px; font-weight:bold; font-size:1.2em;" id="userName"></div>
        </div>
        <div id="scoreList"></div>
      </div>
    </div>

    <script>
      // 網頁載入完成後,自動執行
      window.onload = function() {
        google.script.run
          .withSuccessHandler(showResult)
          .withFailureHandler(showError)
          .getMyGrade();
      };

      function showResult(response) {
        document.getElementById('loading').style.display = 'none';
        
        if (response.status === 'success') {
          document.getElementById('userEmail').innerText = response.email;
          document.getElementById('userName').innerText = response.result[1]; // 假設 B欄是姓名
          
          var html = '';
          // 從 C欄 (Index 2) 開始顯示
          for (var i = 2; i < response.header.length; i++) {
            if(response.header[i]) {
              html += '<div class="item">';
              html += '<span>' + response.header[i] + '</span>';
              html += '<span class="score">' + response.result[i] + '</span>';
              html += '</div>';
            }
          }
          document.getElementById('scoreList').innerHTML = html;
          document.getElementById('resultArea').style.display = 'block';
        } else if (response.status === 'failed') {
          var msg = '嗨 ' + response.email + ',成績單中找不到您的資料。<br>請確認您是否使用學校信箱登入。';
          showError(msg);
        } else {
          showError(response.message);
        }
      }

      function showError(msg) {
        document.getElementById('loading').style.display = 'none';
        var errDiv = document.getElementById('errorMsg');
        errDiv.innerHTML = msg || '發生未知錯誤';
        errDiv.style.display = 'block';
      }
    </script>
  </body>
</html>

關鍵設定:網域權限

這是能抓到 Email 的關鍵。設定錯誤會導致 getActiveUser() 回傳空值。

部署設定 (Deployment Settings)

  • 執行身分 (Execute as):我 (Me)
  • 誰可以存取 (Who has access):任何 [gapp.ylsh.ilc.edu.tw] 的使用者

注意:選項必須選擇「任何 [gapp.ylsh.ilc.edu.tw] 的使用者」,而不能選「任何使用者 (Anyone)」。因為選「任何使用者」時,Google 為了隱私,不會將訪客的 Email 傳給程式。只有在同網域內互信的情況下,才會傳送 Email。

模擬體驗

假設您是學生,已經登入學校 Google 帳號。當您打開網頁時,會發生什麼事?

script.google.com/macros/s/...

正在驗證您的身分...