Skip to Content
开发者中心OAuth 对接指南

OAuth 对接指南

让你的应用支持”用知新芸账号登录”。本页面向开发者,含完整代码示例。如果你只想了解 OAuth 是什么、有哪些能力,请先看 认证 → OAuth 开放

接入前提

  1. 联系知新芸管理员,在 认证管理后台 注册一个 OAuth 应用,获得:
    • client_id
    • client_secret(只展示一次,妥善保存)
    • 已配置的回调地址(必须与下面代码中的 redirect_uri 完全一致)
  2. 准备一个能接收 GET 请求的回调端点(例如 https://your-app.com/api/auth/callback/zxyun

生产环境的 redirect_uri 必须是 HTTPS。本地开发可用 http://localhost:xxxx,但要在管理后台单独添加。


端点速查

用途方法端点
授权页(用户跳转)GEThttps://auth.z-xin.net/oauth2/authorize
Token 交换POSThttps://auth.z-xin.net/api/oauth2/token
用户信息GEThttps://auth.z-xin.net/api/oauth2/userinfo
Swagger 全量 APIhttps://auth.z-xin.net/api-docs

授权码流程(标准 4 步)

1. 把用户跳到知新芸授权页

https://auth.z-xin.net/oauth2/authorize ?client_id=<你的 client_id> &redirect_uri=<你的回调地址,URL 编码> &response_type=code &scope=profile &state=<随机字符串,防 CSRF>

用户在知新芸登录并同意授权后,会带着 code 跳回你的 redirect_uri

https://your-app.com/api/auth/callback/zxyun?code=xxx&state=xxx

2. 验证 state、用 code 换 access_token

⚠️ 这一步必须在后端完成,不要在前端暴露 client_secret

curl -X POST https://auth.z-xin.net/api/oauth2/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=authorization_code" \ -d "code=<上一步的 code>" \ -d "redirect_uri=<同样的回调地址>" \ -d "client_id=<你的 client_id>" \ -d "client_secret=<你的 client_secret>"

返回:

{ "access_token": "xxx", "refresh_token": "xxx", "token_type": "Bearer", "expires_in": 604800, "scope": "profile" }

3. 用 access_token 拉用户信息

curl https://auth.z-xin.net/api/oauth2/userinfo \ -H "Authorization: Bearer <access_token>"

返回:

{ "id": "123456789", "username": "lisi", "name": "李四", "email": "lisi@example.com", "school": "xxx 大学", "roles": ["teacher"] }

4. 在你的系统建立会话

把用户信息写入你自己的 session / JWT,OAuth 流程结束。


Next.js (App Router) 示例

// app/api/auth/callback/zxyun/route.ts import { NextRequest, NextResponse } from 'next/server' import { cookies } from 'next/headers' export async function GET(req: NextRequest) { const code = req.nextUrl.searchParams.get('code') const state = req.nextUrl.searchParams.get('state') const cookieStore = await cookies() const savedState = cookieStore.get('oauth_state')?.value if (!code || state !== savedState) { return NextResponse.redirect(new URL('/login?error=invalid', req.url)) } // 换 token const tokenRes = await fetch('https://auth.z-xin.net/api/oauth2/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code, redirect_uri: process.env.OAUTH_REDIRECT_URI!, client_id: process.env.OAUTH_CLIENT_ID!, client_secret: process.env.OAUTH_CLIENT_SECRET!, }), }) const { access_token } = await tokenRes.json() // 拉用户信息 const userRes = await fetch('https://auth.z-xin.net/api/oauth2/userinfo', { headers: { Authorization: `Bearer ${access_token}` }, }) const user = await userRes.json() // 写入你自己的 session(示例用 cookie) cookieStore.set('app_session', JSON.stringify({ id: user.id, name: user.name }), { httpOnly: true, secure: true, sameSite: 'lax', maxAge: 7 * 24 * 60 * 60, }) return NextResponse.redirect(new URL('/', req.url)) }

Node.js (Express) 示例

import express from 'express' import fetch from 'node-fetch' const app = express() app.get('/auth/callback', async (req, res) => { const { code, state } = req.query if (state !== req.session.oauthState) return res.status(400).send('Invalid state') const tokenRes = await fetch('https://auth.z-xin.net/api/oauth2/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code, redirect_uri: process.env.OAUTH_REDIRECT_URI, client_id: process.env.OAUTH_CLIENT_ID, client_secret: process.env.OAUTH_CLIENT_SECRET, }), }) const { access_token } = await tokenRes.json() const user = await fetch('https://auth.z-xin.net/api/oauth2/userinfo', { headers: { Authorization: `Bearer ${access_token}` }, }).then(r => r.json()) req.session.user = user res.redirect('/') })

Python (Flask) 示例

import os import requests from flask import Flask, request, session, redirect app = Flask(__name__) @app.route('/auth/callback') def callback(): code = request.args.get('code') state = request.args.get('state') if state != session.get('oauth_state'): return 'Invalid state', 400 token = requests.post( 'https://auth.z-xin.net/api/oauth2/token', data={ 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': os.environ['OAUTH_REDIRECT_URI'], 'client_id': os.environ['OAUTH_CLIENT_ID'], 'client_secret': os.environ['OAUTH_CLIENT_SECRET'], }, ).json()['access_token'] user = requests.get( 'https://auth.z-xin.net/api/oauth2/userinfo', headers={'Authorization': f'Bearer {token}'}, ).json() session['user'] = user return redirect('/')

Token 刷新

access_token 默认 7 天有效。临过期前用 refresh_token 续期:

curl -X POST https://auth.z-xin.net/api/oauth2/token \ -d "grant_type=refresh_token" \ -d "refresh_token=<上次拿到的 refresh_token>" \ -d "client_id=<client_id>" \ -d "client_secret=<client_secret>"

常见问题

报错 invalid_redirect_uri 回调地址必须与管理后台注册的完全一致(含端口、路径、尾斜杠)。

报错 invalid_client 检查 client_secret 是否泄露后被管理员重置。

用户信息字段缺少 email / school 申请应用时未勾选对应 scope,或对应字段在用户档案中为空。

Last updated on