feat(mail): 邮箱登录换取会话 token,JMAP 调用不再传输明文密码
- 登录调 /API/Mail/Login 换取 token(密码仅一次传输) - JmapClient 改持 token;登出吊销 token
This commit is contained in:
1 parent
600ca27cbb
commit
86cd82024f
3 files changed
+22
-10
No files matched your search
+14
-2
@@ -40,6 +40,12 @@ export interface JmapProxyResult {
|
||||
}
|
||||
}
|
||||
|
||||
/** 邮箱登录结果 */
|
||||
export interface MailLoginResult {
|
||||
Token: string
|
||||
MailboxName: string
|
||||
}
|
||||
|
||||
/** 邮箱 API 客户端 */
|
||||
export const mailApi = {
|
||||
/** 获取 JMAP 连接配置 */
|
||||
@@ -55,8 +61,14 @@ export const mailApi = {
|
||||
/** 修改邮箱密码 */
|
||||
changePassword: (NewPassword: string): Promise<void> => post<void>('/API/Mail/ChangePassword', { NewPassword }),
|
||||
|
||||
/** JMAP 收发代理(后端统一转发,避免浏览器跨域) */
|
||||
jmap: (data: { Password: string; MethodCalls: Array<[string, Record<string, unknown>, string]>; AccountId?: string }): Promise<JmapProxyResult> =>
|
||||
/** 邮箱登录:校验密码并换取会话 token(密码仅此一次传输,后端存内存) */
|
||||
login: (Password: string): Promise<MailLoginResult> => post<MailLoginResult>('/API/Mail/Login', { Password }),
|
||||
|
||||
/** 邮箱登出:吊销会话 token */
|
||||
logout: (Token: string): Promise<void> => post<void>('/API/Mail/Logout', { Token }),
|
||||
|
||||
/** JMAP 收发代理(携带会话 token,不再传输明文密码) */
|
||||
jmap: (data: { Token: string; MethodCalls: Array<[string, Record<string, unknown>, string]>; AccountId?: string }): Promise<JmapProxyResult> =>
|
||||
post<JmapProxyResult>('/API/Mail/Jmap', data),
|
||||
|
||||
/** 管理员:全部邮箱列表 */
|
||||
|
||||
+5
-5
@@ -49,12 +49,12 @@ export interface JmapCallResult {
|
||||
|
||||
export class JmapClient {
|
||||
readonly mailboxName: string
|
||||
readonly password: string
|
||||
readonly token: string
|
||||
private accountId = ''
|
||||
|
||||
constructor(mailboxName: string, password: string) {
|
||||
constructor(mailboxName: string, token: string) {
|
||||
this.mailboxName = mailboxName
|
||||
this.password = password
|
||||
this.token = token
|
||||
}
|
||||
|
||||
/** 登录验证:通过一次 Mailbox/get 触发后端发现账户 ID */
|
||||
@@ -62,10 +62,10 @@ export class JmapClient {
|
||||
await this.request([['Mailbox/get', {}, 'c0']])
|
||||
}
|
||||
|
||||
/** 批量调用 JMAP 方法(经后端代理) */
|
||||
/** 批量调用 JMAP 方法(经后端代理,携带会话 token,密码不落前端) */
|
||||
async request(methodCalls: Array<[string, Record<string, unknown>, string]>): Promise<JmapCallResult> {
|
||||
const result = await mailApi.jmap({
|
||||
Password: this.password,
|
||||
Token: this.token,
|
||||
MethodCalls: methodCalls,
|
||||
AccountId: this.accountId || undefined,
|
||||
})
|
||||
|
||||
@@ -30,7 +30,6 @@ const applyForm = ref({ MailboxName: '', Password: '', ConfirmPassword: '' })
|
||||
|
||||
// JMAP 会话
|
||||
const client = ref<JmapClient | null>(null)
|
||||
const jmapPassword = ref('')
|
||||
const loginVisible = ref(false)
|
||||
const loginForm = ref({ Password: '' })
|
||||
const connecting = ref(false)
|
||||
@@ -107,10 +106,11 @@ async function submitLogin(): Promise<void> {
|
||||
}
|
||||
connecting.value = true
|
||||
try {
|
||||
const c = new JmapClient(myAddress.value, loginForm.value.Password)
|
||||
// 后端校验密码并签发会话 token(密码仅此一次传输,后端存内存;后续 JMAP 调用只带 token)
|
||||
const result = await mailApi.login(loginForm.value.Password)
|
||||
const c = new JmapClient(result.MailboxName, result.Token)
|
||||
await c.discover()
|
||||
client.value = c
|
||||
jmapPassword.value = loginForm.value.Password
|
||||
loginVisible.value = false
|
||||
loginForm.value.Password = ''
|
||||
await loadMailboxes()
|
||||
|
||||
Reference in new issue
Block a user