用户授权信息获取流程

OAuth 2.0 授权流程及微信小程序用户信息获取实现

问题

描述前端获取用户授权信息的完整流程,包括 OAuth 2.0 授权码模式和微信小程序授权。

解答

OAuth 2.0 授权码模式流程

┌──────────┐                               ┌──────────┐
│   用户   │                               │ 第三方应用 │
└────┬─────┘                               └────┬─────┘
     │                                          │
     │  1. 点击第三方登录                         │
     │─────────────────────────────────────────>│
     │                                          │
     │  2. 重定向到授权服务器                     │
     │<─────────────────────────────────────────│
     │                                          │
     │  3. 用户登录并授权                        │
     │─────────────────────────────────────────>│ 授权服务器
     │                                          │
     │  4. 返回授权码 code                       │
     │<─────────────────────────────────────────│
     │                                          │
     │  5. 携带 code 请求后端                    │
     │─────────────────────────────────────────>│ 后端服务器
     │                                          │
     │  6. 后端用 code 换 access_token           │
     │                                          │
     │  7. 用 token 获取用户信息                 │
     │                                          │
     │  8. 返回用户信息给前端                    │
     │<─────────────────────────────────────────│

前端实现(以 GitHub 登录为例)

// 1. 跳转到授权页面
function redirectToAuth() {
  const clientId = 'your_client_id'
  const redirectUri = encodeURIComponent('https://yoursite.com/callback')
  const scope = 'user:email'
  
  window.location.href = 
    `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}`
}

// 2. 回调页面处理授权码
function handleCallback() {
  const urlParams = new URLSearchParams(window.location.search)
  const code = urlParams.get('code')
  
  if (code) {
    // 将 code 发送给后端换取用户信息
    fetchUserInfo(code)
  }
}

// 3. 请求后端获取用户信息
async function fetchUserInfo(code) {
  const response = await fetch('/api/auth/github', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ code })
  })
  
  const userInfo = await response.json()
  // 存储用户信息和 token
  localStorage.setItem('user', JSON.stringify(userInfo))
}

微信小程序授权流程

// 1. 获取用户登录凭证 code
async function login() {
  const { code } = await wx.login()
  
  // 2. 发送 code 到后端,换取 openid 和 session_key
  const { data } = await wx.request({
    url: 'https://yourserver.com/api/wx/login',
    method: 'POST',
    data: { code }
  })
  
  // 存储自定义登录态
  wx.setStorageSync('token', data.token)
}

// 3. 获取用户信息(需用户点击授权按钮)
function getUserProfile() {
  wx.getUserProfile({
    desc: '用于完善用户资料',
    success: (res) => {
      const { userInfo, encryptedData, iv } = res
      
      // 发送加密数据到后端解密
      wx.request({
        url: 'https://yourserver.com/api/wx/userinfo',
        method: 'POST',
        data: { encryptedData, iv },
        header: { Authorization: wx.getStorageSync('token') }
      })
    }
  })
}
<!-- 小程序 WXML:必须通过按钮触发 -->
<button bindtap="getUserProfile">获取用户信息</button>

后端处理示例(Node.js)

// 用 code 换取 access_token 和用户信息
async function githubAuth(code) {
  // 1. 用 code 换 access_token
  const tokenRes = await fetch('https://github.com/login/oauth/access_token', {
    method: 'POST',
    headers: { Accept: 'application/json' },
    body: JSON.stringify({
      client_id: process.env.GITHUB_CLIENT_ID,
      client_secret: process.env.GITHUB_CLIENT_SECRET,
      code
    })
  })
  
  const { access_token } = await tokenRes.json()
  
  // 2. 用 access_token 获取用户信息
  const userRes = await fetch('https://api.github.com/user', {
    headers: { Authorization: `Bearer ${access_token}` }
  })
  
  return userRes.json()
}

关键点

  • 授权码模式:code 只能使用一次,且有效期短(通常几分钟)
  • 前后端分离:code 换 token 必须在后端完成,避免暴露 client_secret
  • 微信小程序:wx.login 获取 code,wx.getUserProfile 需用户主动点击触发
  • 安全存储:access_token 存后端,前端只存自定义的登录态 token
  • state 参数:OAuth 授权时应携带随机 state 防止 CSRF 攻击