在微信小程序中使用 Cookie
共 7992字,需浏览 16分钟
·
2023-10-14 12:55
和Web相比,微信小程序有哪些限制?
微信小程序作为一种独立的应用开发平台,与传统的 Web 应用相比,存在一些特定的限制和差异。了解这些限制对于在微信小程序中集成身份认证平台至关重要。在本节中,我们将介绍微信小程序相对于 Web 应用的主要限制。
以下是一些微信小程序的限制:
运行环境限制:微信小程序运行在微信客户端中,无法直接在浏览器中访问。因此,一些基于浏览器的功能(如 Cookie 等)在微信小程序中不可用。
网络请求限制:微信小程序中的网络请求受到严格的安全策略限制。只能发送 HTTPS 请求,且只能访问特定的域名。这意味着在集成身份认证平台时,需要确保认证服务提供商的域名在微信小程序的白名单之中。
文件系统限制:微信小程序具有受限的文件系统访问权限。只能访问小程序自身的文件系统,无法直接读取用户本地文件或系统文件。
UI 组件限制:微信小程序提供了一组特定的 UI 组件,相对于 Web 应用的 UI 组件更加有限。需要根据微信小程序的组件库进行布局和样式设计。
代码运行限制:微信小程序使用的是基于 JavaScript 的开发语言,但与 Web 应用相比,微信小程序的 JavaScript 运行环境具有一些差异。需要熟悉微信小程序的开发规范和限制,以确保身份认证功能的正确实现。
通过自定义 Cookie 存储对接网络请求的 set-cookie
尽管在微信小程序中没有像浏览器那样的原生 Cookie 存储,但如果有需要,比如在和身份认证平台交互过程中,非常需要客户端有 Cookie 存取功能的话,可以使用自定义的 Cookie 存储来实现。
好在,不用完全从0开始造轮子,我们可以基于 tough-cookie
封闭几个常用的接口,以便在微信小程序中灵活地读取和存储网络请求中的 Cookie,不妨将自己封装的文件命名为 browser-cookie.ts。
规格说明
我们用一个测试文件说明对 browser-cookie 的使用:
import {Cookie} from "tough-cookie";
import * as assert from "assert";
import {BrowserCookieStore} from "./browser-cookie";
describe('Browser Cookie Store', () => {
const sut = new BrowserCookieStore()
it('finds cookie', (done) => {
sut.findCookie("test", "test", "key", (_err: Error | null, cookie: Cookie | null) => {
expect(cookie).toEqual(null);
done()
});
})
it('finds cookies', (done) => {
sut.findCookies("test", "test", false, (_err: Error | null, cookies: Cookie[] | null) => {
expect(cookies).toBeDefined()
expect(cookies!.length).toEqual(0)
done()
});
})
it('gets all cookies', (done) => {
sut.getAllCookies((_err: Error | null, cookies: Cookie[]) => {
expect(cookies.length).toEqual(0)
done()
})
})
it('puts cookie', (done) => {
sut.putCookie(Cookie.parse("foo=bar")!, (err: Error | null) => {
expect(err).toEqual(null)
sut.getAllCookies((_err, cookies) => {
expect(cookies.length).toEqual(1)
sut.putCookie(Cookie.parse("joe=doe")!, (err2) => {
expect(err2).toEqual(null)
sut.getAllCookies((_, allCookies) => {
expect(allCookies.length).toEqual(2)
})
})
done()
})
})
})
it('removes cookie', (done) => {
sut.getAllCookies((_err, cookies) => {
expect(cookies.length).toEqual(2)
sut.removeCookie('', '', 'foo', (err2) => {
expect(err2).toEqual(null)
sut.getAllCookies((_, allCookies) => {
expect(allCookies.length).toEqual(1)
done()
})
})
})
})
it('removes all cookies', (done) => {
sut.getAllCookies((_err, cookies) => {
expect(cookies.length).toEqual(1)
sut.removeCookies('', '', () => {
sut.getAllCookies((_, allCookies) => {
expect(allCookies.length).toEqual(0);
done();
})
})
})
})
it('update cookie', (done) => {
const cookie = Cookie.parse(`foo=bar`)
assert.ok(cookie)
sut.putCookie(cookie, () => {
sut.getAllCookies((_, allCookies) => {
expect(allCookies.length).toEqual(1)
expect(document.cookie).toEqual(`foo=bar`)
const newCookie = Cookie.parse(`foo=doe`)
assert.ok(newCookie)
sut.updateCookie(cookie, newCookie, () => {
expect(document.cookie).toEqual(`foo=doe`)
done()
})
})
})
})
it('finds cookie without allowSpecialUseDomain', (done) => {
const cb = (err, cookies) => {
expect(err).toEqual(null)
expect(cookies.length).toEqual(0)
done()
}
sut.findCookies('www.zhihu.com', '/api/v3/oauth/captcha', cb)
})
})
实现
import {Cookie, CookieJar, Store} from "tough-cookie";
type FindCookiesCallback = (err: (Error | null), cookie: Cookie[]) => void
export class BrowserCookieStore implements Store {
synchronous: boolean;
findCookie(domain: string, path: string, key: string, cb: (err: (Error | null), cookie: (Cookie | null)) => void): void {
const decodedCookie = decodeURIComponent(document.cookie)
decodedCookie.split(';').forEach(c => {
while (c.startsWith(' ')) {
c = c.substring(1)
}
const name = key + '='
if (c.startsWith(name)) {
cb(null, Cookie.parse(c.substring(name.length, c.length)) ?? null)
}
});
cb(null, null)
}
findCookies(domain: string, path: string, cb: FindCookiesCallback): void
findCookies(domain: string, path: string, allowSpecialUseDomain: boolean, cb: FindCookiesCallback): void
findCookies(domain: string, path: string, allowSpecialUseDomain: boolean | FindCookiesCallback, cb?: FindCookiesCallback): void {
if (!cb) {
cb = allowSpecialUseDomain as FindCookiesCallback
}
const decodedCookie = decodeURIComponent(document.cookie)
const cookies: Cookie[] = []
decodedCookie.split(';').forEach(c => {
const cookie = Cookie.parse(c)
if (cookie) {
if (domain && path) {
if (cookie.domain === domain && cookie.path === path) {
cookies.push(cookie)
}
} else {
cookies.push(cookie)
}
}
})
cb(null, cookies)
}
getAllCookies(cb: (err: (Error | null), cookie: Cookie[]) => void): void {
this.findCookies('', '', false, cb)
}
putCookie(cookie: Cookie, cb: (err: (Error | null)) => void): void {
console.log('put cookie = ', cookie);
document.cookie = cookie.toString()
cb(null)
}
removeCookie(domain: string, path: string, key: string, cb: (err: (Error | null)) => void): void {
this.getAllCookies((_, allCookies) => {
allCookies.forEach(c => {
if (c.key === key) {
document.cookie = `${c.key}=${c.value};max-age=0;`
}
})
cb(null)
});
}
removeCookies(domain: string, path: string, cb: (err: (Error | null)) => void): void {
this.getAllCookies((_, allCookies) => {
allCookies.forEach(c => {
document.cookie = `${c.key}=${c.value};max-age=0`
})
cb(null)
})
}
updateCookie(oldCookie: Cookie, newCookie: Cookie, cb: (err: (Error | null)) => void): void {
this.removeCookie('', '', oldCookie.key, () => {
this.putCookie(newCookie, () => {
cb(null)
})
})
}
}
const cookieStore = new BrowserCookieStore()
cookieStore.synchronous = true;
export const getCookieStore = () => {
return cookieStore
}
export const clearCookieStore = () => {
cookieStore.removeCookies('', '', () => {
})
return cookieStore
}
export const getCookieJar = () => {
return new CookieJar(cookieStore)
}
实际用途
很多网站的登录状态都依赖 Cookie,通过在微信小程序里利用网站 cookie,就可以实现从微信小程序和网站共享登录状态,有一种跨越设备的单点登录效果。