在Word插件开发中,登录授权是常见需求,但Office插件运行在沙箱环境中,无法直接完成完整的网页OAuth授权流程,因此需要借助跨应用跳转,将授权请求转发到外部浏览器,再通过回调将授权结果传回Word插件。下面详细介绍Python实现该场景的完整方案。

一、整体实现逻辑
跨应用跳转授权的核心流程分为三步:
- Word插件触发登录请求,调用Python后端接口获取授权跳转地址
- 插件打开外部浏览器访问授权地址,用户在浏览器完成登录授权,授权服务器回调Python后端
- Python后端处理回调,将授权凭证传递给Word插件,插件完成登录状态存储
二、Python后端服务搭建
后端需要承担两个核心职责:生成授权跳转链接、处理授权回调。这里使用Flask框架实现,假设我们对接的是标准的OAuth2授权服务。
1. 基础环境准备
先安装依赖库:
# 安装必要依赖 pip install flask requests
2. 生成授权跳转地址接口
该接口供Word插件调用,返回需要跳转的授权地址,同时记录本次请求的state参数用于后续校验。
from flask import Flask, request, jsonify
import requests
import uuid
app = Flask(__name__)
# OAuth2配置,替换为实际的授权服务参数
OAUTH_CONFIG = {
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"auth_url": "https://auth.ipipp.com/oauth/authorize",
"token_url": "https://auth.ipipp.com/oauth/token",
"redirect_uri": "http://127.0.0.1:5000/oauth/callback",
"scope": "read_user"
}
# 临时存储state和对应插件标识的映射,生产环境可替换为Redis等存储
state_mapping = {}
@app.route("/api/get_auth_url", methods=["GET"])
def get_auth_url():
# 生成唯一state参数,防止CSRF攻击
state = str(uuid.uuid4())
# 关联当前插件的标识,比如用户ID或者插件实例ID,这里简化为随机字符串
plugin_id = request.args.get("plugin_id", str(uuid.uuid4()))
state_mapping[state] = plugin_id
# 拼接授权跳转地址
auth_url = f"{OAUTH_CONFIG['auth_url']}?client_id={OAUTH_CONFIG['client_id']}&redirect_uri={OAUTH_CONFIG['redirect_uri']}&response_type=code&scope={OAUTH_CONFIG['scope']}&state={state}"
return jsonify({
"code": 0,
"data": {
"auth_url": auth_url,
"state": state
}
})3. 授权回调处理接口
用户授权后,授权服务器会跳转到该接口,后端拿到授权码后换取token,再将结果传递给Word插件。
@app.route("/oauth/callback", methods=["GET"])
def oauth_callback():
code = request.args.get("code")
state = request.args.get("state")
# 校验state合法性
if state not in state_mapping:
return "授权失败:state参数不合法", 400
plugin_id = state_mapping.pop(state)
# 用授权码换取access_token
token_res = requests.post(
OAUTH_CONFIG["token_url"],
data={
"client_id": OAUTH_CONFIG["client_id"],
"client_secret": OAUTH_CONFIG["client_secret"],
"code": code,
"redirect_uri": OAUTH_CONFIG["redirect_uri"],
"grant_type": "authorization_code"
}
)
if token_res.status_code != 200:
return "授权失败:获取token异常", 400
token_data = token_res.json()
# 这里可以将token和plugin_id关联存储,比如存入数据库
# 实际场景中可以通过本地服务、WebSocket等方式将token传递给Word插件
# 这里简化为返回提示,实际可结合插件侧的轮询或者本地端口监听实现传递
return f"授权成功,请将以下token配置到插件:{token_data.get('access_token')}"三、Word插件侧实现
Word插件(以Office JS插件为例)需要完成两个操作:调用后端获取授权地址并打开浏览器、接收后端返回的授权结果。
1. 触发授权跳转
插件中点击登录按钮时,调用Python后端的/api/get_auth_url接口,拿到授权地址后打开外部浏览器。
// Word插件JS代码
async function startLogin() {
// 调用Python后端接口获取授权地址
const response = await fetch("http://127.0.0.1:5000/api/get_auth_url?plugin_id=test_plugin_001");
const result = await response.json();
if (result.code === 0) {
// 打开外部浏览器跳转授权地址
window.open(result.data.auth_url, "_blank");
// 启动轮询查询授权结果,或者监听本地端口接收回调
pollAuthResult(result.data.state);
}
}2. 轮询查询授权结果
插件可以定期调用后端接口查询授权是否完成,这里补充后端的查询结果接口和插件轮询逻辑。
Python后端新增查询接口:
# 临时存储授权结果,生产环境替换为持久化存储
auth_result_mapping = {}
@app.route("/api/check_auth_result", methods=["GET"])
def check_auth_result():
state = request.args.get("state")
plugin_id = request.args.get("plugin_id")
# 这里可以根据state或者plugin_id查询存储的授权结果
# 简化示例,实际需要从数据库或缓存中读取
if plugin_id in auth_result_mapping:
return jsonify({
"code": 0,
"data": auth_result_mapping[plugin_id]
})
return jsonify({
"code": 1,
"msg": "授权未完成"
})插件侧轮询逻辑:
async function pollAuthResult(state) {
const timer = setInterval(async () => {
const response = await fetch(`http://127.0.0.1:5000/api/check_auth_result?state=${state}&plugin_id=test_plugin_001`);
const result = await response.json();
if (result.code === 0) {
clearInterval(timer);
// 存储token到插件本地
localStorage.setItem("auth_token", result.data.access_token);
alert("登录成功");
}
}, 2000);
}四、注意事项
- state参数必须做校验,避免CSRF攻击,不要使用固定值
- 回调地址如果是本地服务,需要确保Word插件和Python后端在同一网络环境,或者回调地址是公网可访问的
- 生产环境中,token存储和传递需要使用更安全的方案,比如加密存储、短期token加刷新机制
- 如果Word插件是VSTO类型,调用外部浏览器和接收回调的逻辑会有差异,但整体跨应用跳转思路一致
以上就是Python实现Word插件登录授权跨应用跳转的完整方案,开发者可以根据实际对接的授权服务调整参数,适配自己的业务场景。