You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift实现EWS GetItem操作持续返回401错误求助

解决Swift调用EWS GetItem时的401未授权错误

你遇到的401错误核心原因是Office 365的EWS早已弃用传统的用户名密码基本认证,现在必须使用OAuth 2.0完成身份验证。你的现有代码缺少正确的授权头配置,这才导致服务拒绝了请求。下面一步步帮你解决这个问题:

第一步:明确OAuth 2.0是必选项

微软多年前就停止支持EWS的基本认证,所有针对Office 365的EWS请求都必须携带有效的OAuth 2.0访问令牌。你需要先在Azure Active Directory中注册一个应用,获取以下关键参数:

  • 租户ID(Tenant ID)
  • 客户端ID(Client ID)
  • 配置正确的EWS权限(添加Exchange Web Services的FullAccess或对应需求的权限,且需管理员同意该权限)

第二步:用MSAL获取OAuth访问令牌

推荐使用微软官方的MSAL库(Microsoft Authentication Library)处理令牌的获取与刷新。先通过Swift Package Manager或CocoaPods添加依赖,再参考以下代码获取令牌:

import MSAL

func getEWSAccessToken(completion: @escaping (String?, Error?) -> Void) {
    let config = MSALPublicClientApplicationConfig(
        clientId: "你的客户端ID",
        redirectUri: "你的重定向URI",
        authority: "https://login.microsoftonline.com/你的租户ID"
    )
    
    guard let application = try? MSALPublicClientApplication(configuration: config) else {
        completion(nil, NSError(domain: "EWS", code: -1, userInfo: [NSLocalizedDescriptionKey: "MSAL初始化失败"]))
        return
    }
    
    // EWS所需的权限范围
    let scopes = ["https://outlook.office365.com/EWS.AccessAsUser.All"]
    
    let parameters = MSALInteractiveTokenParameters(scopes: scopes)
    parameters.promptType = .selectAccount
    
    application.acquireToken(with: parameters) { result, error in
        if let error = error {
            completion(nil, error)
            return
        }
        
        guard let result = result, let accessToken = result.accessToken else {
            completion(nil, NSError(domain: "EWS", code: -2, userInfo: [NSLocalizedDescriptionKey: "未获取到访问令牌"]))
            return
        }
        
        completion(accessToken, nil)
    }
}

第三步:修改GetItem请求,添加授权头

拿到访问令牌后,将其加入请求的Authorization头,同时确保SOAP请求格式合规。修正后的getItem方法如下:

func getItem(with accessToken: String) {
    let url = URL(string: "https://outlook.office365.com/EWS/Exchange.asmx")!
    // 确保xmlString()生成符合EWS规范的SOAP请求,包含正确的ItemId等节点
    let message = xmlString()
    guard let data = message.data(using: .utf8) else {
        print("SOAP消息转Data失败")
        return
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = data
    request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
    // 关键:添加OAuth授权头
    request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            print("请求错误:\(error.localizedDescription)")
            return
        }
        
        guard let httpResponse = response as? HTTPURLResponse else {
            print("无效响应")
            return
        }
        
        if httpResponse.statusCode == 200 {
            if let data = data, let responseString = String(data: data, encoding: .utf8) {
                print("请求成功!响应内容:\(responseString)")
            }
        } else {
            print("响应状态码:\(httpResponse.statusCode)")
            if let data = data, let errorString = String(data: data, encoding: .utf8) {
                print("错误响应内容:\(errorString)")
            }
        }
    }
    task.resume()
}

第四步:在viewDidLoad中串联调用

最后在viewDidLoad里先获取令牌,再执行GetItem请求:

override func viewDidLoad() {
    super.viewDidLoad()
    
    getEWSAccessToken { accessToken, error in
        if let error = error {
            print("获取令牌失败:\(error.localizedDescription)")
            return
        }
        
        guard let accessToken = accessToken else {
            print("无可用访问令牌")
            return
        }
        
        DispatchQueue.main.async {
            self.getItem(with: accessToken)
        }
    }
}

额外检查点

  • 确认xmlString()生成的SOAP请求格式正确,包含完整的<soap:Envelope>、<soap:Body>以及合法的GetItem操作节点和ItemId。
  • 如果是企业租户应用,确保管理员已同意你注册的应用所请求的EWS权限,否则普通用户无法获取令牌。
  • 检查MSAL配置的重定向URI是否与Azure AD中注册的完全一致,否则会导致令牌获取失败。

内容的提问来源于stack exchange,提问作者Tapas Pal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:48:51