You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JWT验证邮箱遇阻:能否仅通过Token获取用户邮箱?

问题描述

我正在用JWT实现用户邮箱验证功能,逻辑是用户登录时如果未确认邮箱,系统会发送包含JWT的验证邮件,邮件内的URL携带Token,用于调用后端接口完成验证。该逻辑在Postman中通过请求体传入邮箱可正常工作,但在浏览器环境中不知如何实现。现有后端验证代码需要从请求体获取邮箱,再结合URL中的Token进行验证;而生成Token时已将邮箱存入JWT的payload中。请问能否仅通过Token获取用户邮箱,以此适配浏览器场景?


相关代码片段

后端邮箱验证接口代码

confirmEmail = async (req, res, next) => {

    const { email } = req.body
    const param = req.params.token

    const user = await userModel.findOne({email})

    if(!user)
    {
        throw new HttpException(401, 'User not found')
    }

    if(user.confirmed)
    {
        throw new HttpException(401, 'User already confirmed')
    }

    if(!user.confirmed)
    {
        const confirmJWT = jwt.verify(param, process.env.SECRET_JWT)
        if(!confirmJWT)
        {
            throw new HttpException(200, 'Token invalid')
        }

        const result = await userModel.emailConfirmed(email)
    }

    res.send('Database updated.')

}

生成JWT并发送验证邮件代码

if(!user.confirmed)
{
    const emailToken = jwt.sign(
        {
            email: user.email
        },
        process.env.SECRET_JWT,
        {
            expiresIn: '15m'
        }
    )
    
    console.log(emailToken)
    emailModel.verifyEmail(email, emailToken)
    throw new HttpException(401, 'Email not confirmed')
}

前端请求代码

API请求函数

export const verifyEmail = () => {
    try
    {
        return API()
        .post(`/api/confirm/:token`, {}, {
            params: { token: store.user.authToken },
            email: store.user.email
        })
        .then(({data: userData}) => {
            console.log('worked')
        })
    }
    catch(error)
    {
        console.log(error)
    }
}

页面调用代码

import { verifyEmail } from '../../services/authAPI'
import { useUserStore } from '../../stores/user'

const store = useUserStore()

export default {
    data()
    {
        return { email: store.user.email }
    },
    methods: {
        async handleSubmit()
        {
            try
            {
                const response = await verifyEmail(this.email)
            }
            catch(err)
            {
                console.log(err)
            }
        }
    }
}
</script>

解决方案

当然可以仅通过Token获取邮箱,这也是JWT的设计初衷之一——payload可携带非敏感业务信息。只需调整前后端逻辑即可适配浏览器场景:

1. 后端代码修改

不再从请求体获取邮箱,直接从解析后的JWT payload中提取,验证Token合法性后完成邮箱确认:

confirmEmail = async (req, res, next) => {
    const param = req.params.token

    // 验证Token并解析payload,捕获过期/篡改异常
    let confirmJWT
    try {
        confirmJWT = jwt.verify(param, process.env.SECRET_JWT)
    } catch (err) {
        throw new HttpException(401, 'Token invalid or expired')
    }

    // 从payload中提取邮箱
    const { email } = confirmJWT

    const user = await userModel.findOne({email})

    if(!user)
    {
        throw new HttpException(401, 'User not found')
    }

    if(user.confirmed)
    {
        throw new HttpException(401, 'User already confirmed')
    }

    const result = await userModel.emailConfirmed(email)

    res.send('Database updated.')
}

2. 前端代码修改

用户点击邮件链接打开页面时,直接从URL中提取Token,无需依赖登录态或手动输入邮箱:

// 从URL提取Token(根据实际路由格式调整)
const getTokenFromUrl = () => {
    // 若URL格式为 /verify?token=xxx
    const params = new URLSearchParams(window.location.search)
    return params.get('token')
    // 若URL格式为 /api/confirm/:token,使用下方代码
    // return window.location.pathname.split('/').pop()
}

export const verifyEmail = () => {
    try
    {
        const token = getTokenFromUrl()
        return API()
        .post(`/api/confirm/${token}`)
        .then(({data: userData}) => {
            console.log('worked')
        })
    }
    catch(error)
    {
        console.log(error)
    }
}

关键说明

  • 调整后,用户点击验证链接即可直接完成验证,无需额外操作,完全适配浏览器场景
  • 保持JWT的短过期时间(当前15分钟),避免Token被滥用
  • 后端必须捕获jwt.verify的异常,处理Token过期、篡改等情况

内容的提问来源于stack exchange,提问作者DMantas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 13:42:22