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