前言
OpenHarmony 作为万物互联的操作系统,网络连接是最基础的能力。其中,有大量的网络应用需要进行用户认证,那么在 OpenHarmony 的应用开发中,用户登录后,如何在 Http 请求中使用 Cookie 传递用户 token,实现登录状态保持?
需求
在 OpenHarmony 中使用@ohos.net.http模块实现网络请求,我们需要实现网络应用用户登录及登录状态保持,但在接口介绍文档、Sample 中并未提供如何保存及使用 Cookie 的示例。
实现
那么下面就通过一个网络音乐应用的例子(应用详细介绍链接在文末),为大家介绍如何实现用户 Token 传递,服务器使用 Django 框架提供的 django.contrib.auth 模块实现用户登录鉴权。
1、用户登录
服务端:
获取 csrf_token 接口:
def get_csrf_token(request):
csrf_token = get_token(request)
return JsonResponse({'csrf_token': csrf_token})
登录接口:
def login_request(request):
username = request.POST.get('username')
password = request.POST.get('password')
print('login from app')
print(request.POST)
# 使用authenticate函数来认证用户
user = authenticate(request, username=username, password=password)
if user is not None:
# 用户认证成功,使用login函数来记录登录状态
login(request, user)
# 设置一个欢迎信息,可以将其改为HttpResponse或重定向
return JsonResponse({'res':"login success!"})
else:
# 认证失败,返回错误信息
return JsonResponse({'res':"login failed!"})
应用端:
先获取服务器 CSRF token,然后使用用户名密码登录。
login() {
try {
this.httpRequest.request(ServerConstants.CSRF_URL,
(err: Error, data: http.HttpResponse) => {
if (!err) {
console.info('HttpResponse Result:' + data.result);
const jsonObject: object = JSON.parse(data.result as string);
this.csrfToken = jsonObject['csrf_token'];
this.httpRequest.request(ServerConstants.LOGIN_URL,
{
method: http.RequestMethod.POST,
extraData: 'username=' + this.userName + '&password=' + this.password,
header: {
'content-Type': 'application/x-www-form-urlencoded',
'X-CSRFToken': this.csrfToken,
'Cookie': 'csrftoken=' + this.csrfToken + ';'
},
},
(err: Error, data: http.HttpResponse) => {
if (!err) {
console.info('HttpResponse Result:' + data.result);
console.info('HttpResponse code:' + data.responseCode);
if (200 === data.responseCode) {
this.loginInfo = this.userName
this.loginCookie = data.header['set-cookie'];
}
console.info('HttpResponse type:' + JSON.stringify(data.resultType));
console.info('HttpResponse header:' + JSON.stringify(data.header));
this.getCustomListFromServer();
} else {
console.info('HttpResponse error:' + JSON.stringify(err));
}
});
} else {
console.info('HttpResponse error:' + JSON.stringify(err));
}
});
} catch (err) {
console.info('HttpRequest error:' + JSON.stringify(err));
}
}
登录成功后将 Cookie 保存:this.loginCookie = data.header['set-cookie'];
Cookie 内容如下:
- ["csrftoken=VPfo0sQoTzgsRhSrySJsoQZQMxwZWqmAm0Y4L1szUdbwOv41M19tkprs73w6pNrH; expires=Thu, 12 Jun 2025 09:20:42 GMT; Max-Age=31449600; Path=/; SameSite=Lax", "sessionid=e45smklhb4gdaimk3c3pm0kpv1c7u3lq; expires=Thu, 27 Jun 2024 09:20:42 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax" ]
2、后续请求
服务端:
根据用户查询信息的接口:
def get_playlists(request):
print(request.user)
playlists = Playlist.objects.filter(user=request.user).values('playlist_name','song').distinct()
result = []
for playlist in playlists:
song = Song.objects.filter(id=playlist.get('song')).values('id', 'name', 'singer','language').first()
combined_queryset = playlist | song
result.append(combined_queryset)
return JsonResponse(result, safe=False)
应用端:
使用保存的 Cookie,放在请求头中,服务端即能从 request 中获取用户信息。
需要根据请求 Cookie 进行格式转换:
this.loginCookie.map(cookie => {
const parts = cookie.split('=');
return `${parts[0]}=${parts[1]}`;
}).join('; ')
完整接口:
getCustomListFromServer() {
this.customPlayLists = [];
try {
this.httpRequest.request(ServerConstants.CUSTOM_LIST_URL,
{
method: http.RequestMethod.GET,
header: {
'Cookie': this.loginCookie.map(cookie => {
const parts = cookie.split('=');
return `${parts[0]}=${parts[1]}`;
}).join('; ')
}
},
(err: Error, data: http.HttpResponse) => {
if (!err) {
console.info('HttpResponse getCustomListFromServer Result:' + data.result);
interface PlaylistItem {
playlist_name: string;
song: number;
id: number;
name: string;
singer: string;
language: string;
}
let jsonObject: PlaylistItem[] = JSON.parse(data.result as string);
let groupedByPlaylistName = jsonObject.reduce((accumulator, item) => {
if (!accumulator[item.playlist_name]) {
accumulator[item.playlist_name] = [];
}
accumulator[item.playlist_name].push(item);
return accumulator;
}, {} as Record<string, PlaylistItem[]>);
const keys = Object.keys(groupedByPlaylistName);
keys.forEach((playlistName) => {
console.log(`Playlist: ${playlistName}`);
let aPlayingList: AudioItem[] = Array<AudioItem>();
groupedByPlaylistName[playlistName].forEach((song, index) => {
aPlayingList.push(new AudioItem(song.name, song.singer, song.id.toString()));
});
this.customPlayLists.push(new PlayListData(playlistName, $r('app.media.icon'), aPlayingList.length + '首',
aPlayingList, ''));
});
} else {
console.info('HttpResponse error:' + JSON.stringify(err));
}
});
} catch (err) {
console.info('HttpRequest error:' + JSON.stringify(err));
}
}
遗留问题
在开发过程中,参考接口文档时,http 模块提供了一个缓存接口:
按说明理解,应该可以实现自动缓存 cookie 的功能,但是按示例代码尝试后并不生效,不知和本文所需要的功能是否相同,或者说有 bug,有待大家探索。