浣滆€咃細鐜嬫櫒褰?/p>
鍓嶈█
瑕佽鏈€杩戝墠绔妧鏈湀浠€涔堟渶馃敟锛岄偅涓€瀹氶潪銆岄仴閬ラ鍏堛€嶇殑 Harmony OS
鑾睘锛屾湰鐫€瀵规柊鎶€鏈殑杩芥眰绮剧锛堟壎鎴戣捣鏉モ€︹€︼級锛屽挶浠篃鏉ュ皾璇曚竴涓嬨€?/p>
鎬濇潵鎯冲幓锛屽喅瀹氱敤楦胯挋閲嶅啓涓€涓嬩箣鍓嶇殑 Compose 鐗堟湰鐨勩€岀帺 Android銆嶃€?/p>
闂
瀵圭収鐫€瀹樻柟鏁欑▼锛屼竴寮€濮嬮兘鎸洪『鍒╋紝鐩村埌寮€濮嬪啓鐧诲綍妯″潡銆?/p>
鎴戜滑鐭ラ亾锛岄缚娲嬪ぇ浣殑銆岀帺 Android銆岮pi 鏄敤 Cookie
淇濆瓨鐧诲綍鐘舵€佺殑锛岃€岄缚钂欒姹傜綉缁滀娇鐢ㄧ殑 httpRequest 涓嶆敮鎸?Cookie锛屾垜缈婚亶浜嗗畼鏂规枃妗i兘娌℃壘鍒板叧浜?Cookie 鐨勮鏄庛€?/p>
鎴戜笉姝诲績锛屽績鎯筹紝Cookie 杩欎箞甯歌锛屽簲璇ヤ笉姝㈡垜涓€涓汉閬囧埌闂鍚э紝浜庢槸鍦ㄩ缚钂欏畼鏂瑰紑鍙戣€呰鍧涙悳绱?Cookie 鍏抽敭瀛楋紝杩樼湡鏈変汉鎻愰棶
浜庢槸寮€蹇冪殑鐐逛簡杩涘幓锛岀湅鍒扮増涓荤殑鍥炵瓟鍚庯紝鎴戝交搴曟蹇冧簡馃挃
鍏跺疄鑳藉鐞嗚В瀹樻柟涓轰粈涔堜笉鎻愪緵鐩存帴鎿嶄綔 Cookie 鐨勬帴鍙o紝鍥犱负杩欎釜澶€滀笟鍔♀€濅簡锛屽喌涓旓紝Android 瀹樻柟涔熸病鏈夋彁渚涚被浼肩殑鎺ュ彛锛岄渶瑕佸紑婧愮ぞ鍖哄府鍔╁畬鍠勩€?/p>
涓嶈繃鐩墠楦胯挋鐨勫紑婧愬簱灞堟寚鍙暟锛屼篃娌℃湁鎵惧埌缃戠粶鐩稿叧鐨勫紑婧愬簱銆?/p>
闅鹃亾 Harmony 鐗堟湰鐨勩€岀帺 Android銆嶅氨瑕佹姌鎴熶簬姝や簡鍚楋紝鎴栬€呬笉鎻愪緵鐧诲綍鍔熻兘锛?/p>
瑙e喅
淇楄瘽璇村緱濂斤細鍙鎬濇兂涓嶆粦鍧★紝鍔炴硶鎬绘瘮鍥伴毦澶氾紒鏃㈢劧娌℃湁鐜版垚鐨?Cookie 搴擄紝閭f垜浠氨鑷繁鏉ュ疄鐜颁竴涓€?/p>
鍥為【
鎴戜滑鍏堟潵澶嶄範涓?Cookie 鐨勭煡璇?/p>
瀹氫箟
鎽樿嚜缁村熀鐧剧
HTTP Cookie锛岀畝绉?Cookie锛屾槸娴忚缃戠珯鏃剁敱缃戠粶鏈嶅姟鍣ㄥ垱寤哄苟鐢辩綉椤垫祻瑙堝櫒瀛樻斁鍦ㄧ敤鎴疯绠楁満鎴栧叾浠栬澶囩殑灏忔枃鏈枃浠躲€?
缁撴瀯
Cookie鐨勫熀鏈粨鏋勫寘鎷?/p>
- 鍚嶇О
- 鍙栧€?/li>
- 鍚勭灞炴€?/li>
灞炴€?/strong>
涓€鏉?Cookie 鍙兘鏈?Domain
銆?code>Path銆?code>Expires銆?code>Max-Age銆?code>Secure銆?code>HttpOnly 绛夊绉嶅睘鎬э紝濡?/p>
HTTP/1.0 200 OK
Set-Cookie: LSID=DQAAAK鈥aem_vYg; Path=/accounts; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly
Set-Cookie: HSID=AYQEVn鈥Krdst; Domain=.foo.com; Path=/; Expires=Wed, 13 Jan 2021 22:23:01 GMT; HttpOnly
Set-Cookie: SSID=Ap4P鈥TEq; Domain=foo.com; Path=/; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly
鈥?
宸ヤ綔娴佺▼
- 瀹㈡埛绔姹傛湇鍔″櫒鏃讹紝鏈嶅姟鍣ㄩ€氳繃鍝嶅簲澶翠腑鐨?
Set-Cookie
鍏抽敭瀛楀憡璇夊鎴风闇€瑕佷繚瀛樼殑 Cookie 淇℃伅 - 瀹㈡埛绔牴鎹?Cookie 绫诲瀷淇濆瓨 Cookie 淇℃伅
- 涓嬫璇锋眰鏃舵鏌ュ綋鍓?Domain 鍜?Path 鍦ㄦ湰鍦版槸鍚︽湁 Cookie 淇℃伅锛屽鏋滄湁鐨勮瘽锛岄€氳繃璇锋眰澶翠腑鐨?
Cookie
瀛楁灏?Cookie 淇℃伅鍛婅瘔鏈嶅姟鍣?/li>
鍚彂
鏃㈢劧 Cookie 鐨勬帴鏀跺拰鍙戦€侀兘鏄€氳繃 Header
瀹炵幇锛岄偅鎴戜滑涔熷彲浠ヤ粠杩欓噷鍏ユ墜銆?/p>
鍏堢湅鎺ユ敹閮ㄥ垎锛屾垜浠厛鐢?PostMan 鍙戦€佽姹傦紝鐪嬩笅杩斿洖鐨?Cookie锛屼綔涓哄鐓?/p>
鍙互鐪嬪埌锛岀櫥褰曚俊鎭繚瀛樺湪浜?loginUserName
鍜?token_pass
涓?/p>
鎺ヤ笅鏉ワ紝鎴戜滑鎵撳嵃涓嬬敤 httpRequest
鍙戦€佺櫥褰曡姹傜殑鍝嶅簲澶?/p>
{
"content-type": "application/json;charset=UTF-8",
"date": "Wed, 27 Dec 2023 07:50:12 GMT",
"server": "Apache-Coyote/1.1",
"set-cookie": "token_pass_wanandroid_com=******; Domain=wanandroid.com; Expires=Fri, 26-Jan-2024 07:50:12 GMT; Path=/",
"transfer-encoding": "chunked"
}
濂藉浼欙紝鎬庝箞鍙湁 token_pass
锛岄缚钂欎綘璁╂垜璇翠綘浠€涔堝ソ锛屾€庝箞 Cookie 杩樺皯浜?/p>
濂藉湪鎴戝彂鐜?httpRequest
鐨?Response 涓湁涓€涓崟鐙殑 Cookie 瀛楁锛屾墦鍗颁笅鐪嬬湅
www.wanandroid.com FALSE / FALSE 1706255412 loginUserName chaywong
www.wanandroid.com FALSE / FALSE 1706255412 token_pass xxxxxx
.wanandroid.com TRUE / FALSE 1706255412 loginUserName_wanandroid_com chaywong
.wanandroid.com TRUE / FALSE 1706255412 token_pass_wanandroid_com xxxxxx
#HttpOnly_www.wanandroid.com FALSE / TRUE 0 JSESSIONID CDF02AE9203F644EF8E44F8C444AD3B1
杩欐鍊掓槸鍩烘湰鍏ㄤ簡锛屼竴涓崲琛岃〃绀轰竴鏉?Cookie锛屾瘡涓€琛岄€氳繃绌烘牸闅斿紑 Cookie 鐨勫睘鎬э紝鍙槸杩欐瘡涓睘鎬ф槸浠€涔堝惈涔夊晩锛?/p>
鍙堟槸涓€椤跨炕鏂囨。锛岀炕娉ㄩ噴锛屼篃娌℃壘鍒版瘡涓瓧娈电殑璇存槑锛岀湅鏉ュ彧鑳界‖鐫€澶寸毊鐚滀簡
鍏堢湅绗竴琛岋紝wanandroid.com
搴旇鏄?Domain锛?code>/ 搴旇鏄?Path锛?code>1706255412 搴旇鏄繃鏈熸椂闂达紝loginUserName
杩欒鏄?Cookie name锛?code>chaywong 鏄垜鐨勭敤鎴峰悕锛屾墍浠ヨ繖涓簲璇ユ槸 Cookie value锛屽墿涓嬬殑涓や釜 boolean 瀹炲湪娌℃硶鐚滀簡锛屽簲璇ユ槸 Secure 鍜?HttpOnly锛屼絾鏄病鍔炴硶瀵瑰簲銆?/p>
濂藉湪杩欎袱涓睘鎬у苟涓嶉噸瑕侊紝涓嶅奖鍝嶄富瑕佸姛鑳姐€?/p>
鏈変簡鏈嶅姟鍣ㄧ殑 Cookie锛屾垜浠繕瑕佹妸 Cookie 鍐嶅彂閫佺粰鏈嶅姟鍣紝浠ヨ〃鏄庤韩浠斤紝杩欏氨鏇寸畝鍗曚簡
鐩存帴鍦?httpRequest
鐨勮姹傚ご涓鍔?Cookie
瀛楁锛屾牸寮忎负
name1=value1; name2=value2
瀹炶返
鏃㈢劧 Cookie 鐨勬帴鏀跺拰鍙戦€侀兘娌¢棶棰樹簡锛岄偅鎴戜滑灏辨潵瀹炵幇涓€涓缚钂欎笂鐨?CookieJar锛?/p>
涓轰簡灏戣蛋寮矾锛屽疄璺甸儴鍒嗘垜浠洿鎺ュ弬鑰?Android 涓婄殑鏈夊悕鐨勫紑婧愬簱 PersistentCookieJar
锛屽拰 OkHttp
閰嶅悎璧锋潵浣跨敤 Cookie 闈炲父鏂逛究锛屾垜鍦?Compose 鐗堢殑銆岀帺 Android銆?涓婄敤鐨勪篃鏄繖涓柟妗堛€?/p>
棣栧厛瀹氫箟 Cookie 瀵硅薄锛岀敤鏉ヤ繚瀛?Cookie 灞炴€э紝OkHttp 涓凡缁忓畾涔夊ソ浜嗭紝鎴戜滑鐩存帴鎷挎潵鐢?/p>
杞垚 TS 鐗堟湰
/**
* Cookie 瀵硅薄锛屽弬鑰?OkHttp 涓?Cookie 缁撴瀯
*/
export default class Cookie {
name: string = "";
value: string = "";
expiresAt: number = 0;
domain: string = "";
path: string = "";
httpOnly: boolean = false;
persistent: boolean = true;
matches(url: uri.URI): boolean {
let domainMatch = Cookie.domainMatch(url.host, this.domain);
if (!domainMatch) return false;
if (!Cookie.pathMatch(url, this.path)) return false;
return true;
}
createCookieKey(): string {
return `https://${this.domain}${this.path}|${this.name}`;
}
isExpired(): boolean {
let nowTime = Date.now() / 1000;
return this.expiresAt < nowTime;
}
}
鏈変簺灞炴€ф垜浠殏鏃惰幏鍙栦笉鍒帮紝鍙互鍏堝拷鐣ャ€?/p>
鎺ヤ笅鏉ユ槸瑙f瀽 httpRequest
Response 涓殑 Cookie 淇℃伅
/**
* httpRequest 杩斿洖鐨?cookie 鏍煎紡锛?
* ```
* www.wanandroid.com FALSE / FALSE 1706255412 loginUserName chaywong
* www.wanandroid.com FALSE / FALSE 1706255412 token_pass xxxxxx
* .wanandroid.com TRUE / FALSE 1706255412 loginUserName_wanandroid_com chaywong
* .wanandroid.com TRUE / FALSE 1706255412 token_pass_wanandroid_com xxxxxx
* #HttpOnly_www.wanandroid.com FALSE / TRUE 0 JSESSIONID CDF02AE9203F644EF8E44F8C444AD3B1
* ```
*/
static parseHttpRequestCookies(cookieString: string): Array<Cookie> {
if (!cookieString) return [];
let cookies: Array<Cookie> = [];
let lines = cookieString.split("\r\n");
lines.forEach((line) => {
let attrs = line.split("\t");
if (attrs.length !== 7 || !attrs[0]) return;
let cookie = new Cookie();
let domain = attrs[0];
if (domain.startsWith("#HttpOnly_")) {
domain = domain.substring("#HttpOnly_".length);
cookie.httpOnly = true;
}
cookie.domain = domain;
cookie.path = attrs[2];
cookie.expiresAt = parseInt(attrs[4]);
cookie.name = attrs[5];
cookie.value = attrs[6];
cookies.push(cookie);
})
return cookies;
}
鎺ヤ笅鏉ユ槸璁捐 CookieJar
锛屽疄鐜?Cookie 鐨勬帴鏀躲€佸彂閫佸姛鑳斤紝杩欓噷鎴戜滑鍚屾牱鍊熼壌 OkHttp 鐨?CookieJar 鎺ュ彛锛岃繖鍑犱釜鎺ュ彛鍒氬ソ婊¤冻鎴戜滑鎺ユ敹銆佸彂閫併€佹竻闄?Cookie 鐨勮兘鍔涖€?/p>
杞垚 TS 鐗堟湰
export default interface CookieJar {
init(): Promise<void>;
saveFromResponse(url: uri.URI, cookies: Array<Cookie>);
loadForRequest(url: uri.URI): Array<Cookie>;
clear();
}
澧炲姞浜嗕竴涓?init
寮傛鏂规硶涓昏鏄负浜嗗紓姝ュ姞杞?KV 缂撳瓨銆?/p>
鎺ュ彛瀹氫箟濂斤紝鎺ヤ笅鏉ュ氨鏄疄鐜颁簡锛岃繖涓嬭鍙傝€?PersistentCookieJar
浜?/p>
杩欓噷涓昏閫氳繃鍐呭瓨鍜?Preference 鍙岄噸缂撳瓨 Cookie 淇℃伅锛岄€昏緫骞朵笉澶嶆潅锛屾垜浠洿鎺ュ疄鐜?TS 鐗堟湰
export default class PersistentCookieJar implements CookieJar {
private isInit = false;
private cache: HashMap<string, Cookie> = new HashMap();
private persistor: CookiePersistor;
constructor(context: Context) {
this.persistor = new KVCookiePersistor(context);
}
async init(): Promise<void> {
if (!this.isInit) {
let cookies = await this.persistor.loadAll();
cookies.forEach((cookie) => {
this.cache.set(cookie.createCookieKey(), cookie);
})
this.isInit = true;
}
return new Promise<void>((resolve, reject) => {
resolve();
})
}
saveFromResponse(url: uri.URI, cookies: Cookie[]) {
cookies.forEach((cookie) => {
this.cache.set(cookie.createCookieKey(), cookie);
})
this.persistor.saveAll(PersistentCookieJar.filterPersistentCookies(cookies));
}
private static filterPersistentCookies(cookies: Array<Cookie>): Array<Cookie> {
return cookies.filter((item) => {
return item.persistent;
})
}
loadForRequest(url: uri.URI): Cookie[] {
let cookiesToRemove: Array<Cookie> = [];
let validCookies: Array<Cookie> = [];
let iterator: IterableIterator<Cookie> = this.cache.values();
let result = iterator.next();
while (!result.done) {
let currentCookie: Cookie = result.value;
if (currentCookie.isExpired()) {
cookiesToRemove.push(currentCookie);
} else if (currentCookie.matches(url)) {
validCookies.push(currentCookie);
}
result = iterator.next();
}
cookiesToRemove.forEach((item) => {
this.cache.remove(item.createCookieKey());
})
this.persistor.removeAll(cookiesToRemove);
return validCookies;
}
clear() {
this.cache.clear();
this.persistor.clear();
}
}
TS 鐗堢殑 KV 缂撳瓨 KVCookiePersistor
鐨勫叿浣撳疄鐜拌繖閲屽厛鐪佺暐浜嗭紝鍚庨潰璐村畬鏁翠唬鐮併€?/p>
鍒拌繖閲岋紝鎴戜滑宸茬粡瀹炵幇浜嗛缚钂欑増鐨?CookieJar 浜嗭紝鍙渶瑕佸拰 httpRequest
缁撳悎璧锋潵鍗冲彲
鎴戞娊浜嗕竴涓叕鍏辩殑鍙戦€佽姹傜殑鏂规硶锛屽彧闇€瑕佸湪杩欓噷鍔犱笂 CookieJar 鐨勮皟鐢ㄥ嵆鍙€?鍦ㄨ姹傚墠鏌ユ壘鏄惁鏈夊尮閰嶇殑 Cookie锛屽鏋滄湁灏辨坊鍔犲埌 Header 涓紝鍚屾牱鐨勫湪璇锋眰杩斿洖鍚庡瓨鍌ㄦ湇鍔$杩斿洖鐨?Cookie 淇℃伅銆?/p>
let cookieJar: CookieJar = new PersistentCookieJar(EntryContext.getContext());
/**
* 閫氱敤璇锋眰鏂规硶
*/
async function requestSync<T>(path: string, method: http.RequestMethod, extraData?: Object): Promise<Response<T>> {
// 纭繚 CookieJar 宸茬粡鍒濆鍖?
await cookieJar.init();
return new Promise<Response<T>>((resolve, reject) => {
let url = BASE_URL + path;
let uri = parseUri(url);
let header = {};
// 鏍规嵁 url 鍔犺浇鏈湴缂撳瓨鐨?Cookie
let cookies = cookieJar.loadForRequest(uri);
if (cookies.length > 0) {
// 灏?Cookie 娣诲姞鍒?Header 涓?
header["Cookie"] = CookieUtils.cookieHeader(cookies);
}
if (method === http.RequestMethod.POST) {
header["Content-Type"] = "application/x-www-form-urlencoded";
}
let httpRequest = http.createHttp();
httpRequest.request(
url,
{
method: method,
expectDataType: http.HttpDataType.OBJECT,
header: header,
extraData: extraData
},
(err, data) => {
let res = new Response<T>()
if (!err && data.responseCode === 200) {
// 璇锋眰鎴愬姛锛屼繚瀛樻湇鍔$杩斿洖鐨?Cookie
cookieJar.saveFromResponse(uri, CookieUtils.parseHttpRequestCookies(data.cookies))
Object.assign(res, data.result)
} else {
res.errorCode = data.responseCode
res.errorMsg = err.message
}
resolve(res);
}
)
})
}
/**
* 鐧诲綍璇锋眰
*/
async login(username: string, password: string): Promise<Response<User>> {
return requestSync("/user/login", http.RequestMethod.POST, `username=${username}&password=${password}`);
}
杩愯椤圭洰锛岀櫥褰曚竴涓嬶紝鎴戜滑鐨勭櫥褰曠姸鎬佹灉鐒惰淇濆瓨涓嬫潵浜嗭紝鐢变簬閫氳繃 KV 鎸佷箙鍖栦簡 Cookie 淇℃伅锛岄噸鍚?App 鍚庣櫥褰曟€佷粛鐒惰繕鍦ㄣ€?/p>
鎬荤粨
鏈枃涓昏鍒嗕韩鍦ㄥ叆鍧戦缚钂欐椂閬囧埌鐨?Cookie 闂锛岄€氳繃鍊熼壌 Android 涓婄殑 OkHttp
锛屾渶缁堝疄鐜颁簡楦胯挋鐗堢殑 PersistentCookieJar锛岃櫧鐒跺姛鑳戒笉澶熷畬鍠勶紝浣嗗凡缁忚兘澶熻В鍐抽棶棰橈紝鏈熼棿涔熻杩涔犱簡涓€鎶婄綉缁滅煡璇嗐€?/p>