如何用Python的simple_salesforce库通过Microsoft凭证STS登录Salesforce?
问题分析与解决方案
你的Salesforce组织配置了Azure AD作为身份提供商的SAML单点登录(STS),因此标准的用户名+密码+安全令牌登录方式会被组织安全策略拦截,必须采用适配SSO场景的OAuth2认证流程。以下是具体的Python实现方案:
一、先修正Connected App配置
- 回调URL设置:
- 若用授权码流,本地开发可填
http://localhost:8080/callback(需和代码中配置完全一致); - 若用设备码流,可填占位URL如
https://login.salesforce.com/services/oauth2/success(无需实际服务)。
- 若用授权码流,本地开发可填
- OAuth范围:至少勾选
api(用于API调用)、web,需要全权限则选full。 - 权限与IP限制:确保Connected App的网络访问策略允许你的开发机器IP,且权限配置正确。
二、Python实现方案
方案1:设备码流(适合无浏览器的服务器环境)
无需本地浏览器,通过设备码引导用户在任意终端完成SSO登录,脚本自动轮询获取令牌:
import requests import time from simple_salesforce import Salesforce # 配置参数 CLIENT_ID = "你的Connected App Consumer Key" INSTANCE_URL = "https://domain.my.salesforce.com" AUTH_ENDPOINT = f"{INSTANCE_URL}/services/oauth2/device" TOKEN_ENDPOINT = f"{INSTANCE_URL}/services/oauth2/token" # 获取设备码 device_resp = requests.post(AUTH_ENDPOINT, data={ "client_id": CLIENT_ID, "scope": "api full" }).json() print(f"请打开链接登录: {device_resp['verification_uri']}") print(f"输入验证代码: {device_resp['user_code']}") # 轮询等待令牌 interval = device_resp['interval'] device_code = device_resp['device_code'] token_resp = {} while True: token_resp = requests.post(TOKEN_ENDPOINT, data={ "grant_type": "urn:ietf:params:oauth:grant-type:device_code", "client_id": CLIENT_ID, "device_code": device_code }).json() if 'access_token' in token_resp: break elif token_resp['error'] == 'authorization_pending': time.sleep(interval) else: raise Exception(f"认证失败: {token_resp['error_description']}") # 初始化Salesforce客户端并测试 sf = Salesforce( instance_url=token_resp['instance_url'], session_id=token_resp['access_token'] ) print(sf.query("SELECT Id, Name FROM Account LIMIT 1"))
方案2:授权码流(适合有本地浏览器的开发环境)
自动打开本地浏览器引导完成Azure AD SSO,通过临时本地服务接收回调并获取令牌:
from flask import Flask, request from simple_salesforce import Salesforce import webbrowser import requests app = Flask(__name__) # 配置参数 CLIENT_ID = "你的Connected App Consumer Key" CLIENT_SECRET = "你的Connected App Consumer Secret" REDIRECT_URI = "http://localhost:8080/callback" INSTANCE_URL = "https://domain.my.salesforce.com" AUTH_URL = f"{INSTANCE_URL}/services/oauth2/authorize" TOKEN_URL = f"{INSTANCE_URL}/services/oauth2/token" @app.route('/callback') def callback(): code = request.args.get('code') # 交换令牌 token_resp = requests.post(TOKEN_URL, data={ "grant_type": "authorization_code", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "redirect_uri": REDIRECT_URI, "code": code }).json() # 初始化客户端并测试 sf = Salesforce( instance_url=token_resp['instance_url'], session_id=token_resp['access_token'] ) print(sf.query("SELECT Id, Name FROM Account LIMIT 1")) return "登录成功,可关闭此页面" if __name__ == '__main__': # 打开浏览器引导登录 auth_url = f"{AUTH_URL}?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope=api full" webbrowser.open(auth_url) app.run(port=8080)
三、关键注意事项
- 不要尝试用用户名+密码+安全令牌登录,你的组织已强制启用SSO,该方式会被拦截。
- 回调URL必须与代码配置完全一致,包括协议(http/https)和端口。
- 生产环境建议使用HTTPS回调URL,本地开发可使用
http://localhost。
内容的提问来源于stack exchange,提问作者Shubham Khaladkar
相关产品推荐
相关产品推荐

