iOS端基于AppAuth SDK实现OAuth认证后,如何在内部WebView复用认证会话
iOS端基于AppAuth SDK实现OAuth认证后,如何在内部WebView复用认证会话
我之前做项目的时候正好碰到过一模一样的需求——用AppAuth做完OAuth登录后,不想让用户跳外部浏览器,直接在APP内的WebView打开内部页面就能自动登录,不用再输账号密码。当时查了不少资料也踩了些坑,给你分享几个可行的方案:
方案一:通过请求头携带OAuth Token直接验证
这是最直接也最常用的方式,不用纠结cookie共享的问题:
- 用AppAuth拿到
access_token后,在WKWebView加载内部页面之前,通过WKNavigationDelegate的代理方法,给每个发出的请求加上Authorization请求头,值为Bearer {你的access_token} - 后端服务需要做适配:当收到带有这个头的请求时,先验证token的合法性,验证通过后就直接认为用户已登录,无需再走登录流程
举个简单的代码示例:
class WebViewNavigationDelegate: NSObject, WKNavigationDelegate { private let accessToken: String init(accessToken: String) { self.accessToken = accessToken super.init() } func webView(_ webView: WKWebView, willSend request: WKURLRequest, navigation: WKNavigation?) -> WKURLRequest? { guard var mutableRequest = request as? NSMutableURLRequest else { return request } // 给请求添加Authorization头 mutableRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") return mutableRequest as URLRequest } } // 初始化WebView时设置代理 let accessToken = "从AppAuth获取的有效access_token" let delegate = WebViewNavigationDelegate(accessToken: accessToken) let webViewConfig = WKWebViewConfiguration() let webView = WKWebView(frame: view.bounds, configuration: webViewConfig) webView.navigationDelegate = delegate // 加载内部页面 webView.load(URLRequest(url: URL(string: "https://你的内部页面域名")!))
方案二:用Refresh Token换取会话Cookie并注入WebView
如果你的后端依赖cookie来维持会话,那可以通过以下步骤把会话cookie注入到WebView中:
- 用AppAuth获取到的
refresh_token,调用后端专门的接口(比如/api/refresh-session),后端验证refresh token后,返回包含会话cookie的Set-Cookie响应头 - 解析响应头中的cookie信息,然后将cookie注入到WKWebView的
WKHTTPCookieStore中 - 注入完成后再加载内部页面,WebView就会自动携带这个cookie,实现免登录
代码示例大概是这样:
// 1. 调用后端接口换取会话cookie let refreshToken = "从AppAuth获取的refresh_token" let refreshUrl = URL(string: "https://你的后端域名/api/refresh-session")! var request = URLRequest(url: refreshUrl) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") let bodyParams = ["refresh_token": refreshToken] request.httpBody = try? JSONSerialization.data(withJSONObject: bodyParams) URLSession.shared.dataTask(with: request) { [weak self] data, response, error in guard let self = self, let httpResponse = response as? HTTPURLResponse, let cookieFields = httpResponse.allHeaderFields as? [String: String], let cookies = HTTPCookie.cookies(withResponseHeaderFields: cookieFields, for: refreshUrl) else { return } // 2. 初始化WebView并注入cookie let webViewConfig = WKWebViewConfiguration() let webView = WKWebView(frame: self.view.bounds, configuration: webViewConfig) // 注入每个cookie到WebView的cookie存储 let group = DispatchGroup() for cookie in cookies { group.enter() webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie) { group.leave() } } // 3. 所有cookie注入完成后加载页面 group.notify(queue: .main) { webView.load(URLRequest(url: URL(string: "https://你的内部页面域名")!)) self.view.addSubview(webView) } }.resume()
关于默认Cookie无法共享的说明
你之前查到的“认证cookie只能和外部Safari共享,不能和内部WebView共享”是对的——这是苹果的沙盒安全设计导致的:AppAuth使用的ASWebAuthenticationSession或SFSafariViewController,它们的cookie容器和APP内的WKWebView是完全隔离的,系统不会允许直接共享这些cookie,所以不要在这上面浪费时间,直接用上面的两种方案就好。
一些安全注意事项
- 不管用哪种方案,都要确保所有请求都走HTTPS,防止token或cookie被窃听
access_token和refresh_token要存在苹果的Keychain中,不要存在UserDefaults里,避免被窃取- 注入cookie时要注意匹配正确的域名和路径,否则cookie不会生效
内容来源于stack exchange
相关产品推荐
相关产品推荐

