请教个 jwt 鉴权的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
cnfczn
V2EX    Node.js

请教个 jwt 鉴权的问题

  •  
  •   cnfczn 2023-07-11 13:52:54 +08:00 4368 次点击
    这是一个创建于 821 天前的主题,其中的信息可能已经有所发展或是发生改变。

    之前 nodejs 里习惯用 session 做鉴权,验证通过 req.session 直接保存就可以了。后续不管是浏览器请求还是 ajax 都会拿到 session 。 现在打算改成 jwt ,看 w3c 标准说是放到 header 的 Authorization 里边,鉴权逻辑已经改好了,但是传递这个头信息有点麻烦。 例如: /login 验证完,json 返回 token ,后续 ajax 请求可以自己加 header ,但是浏览器直接访问的页面并不会主动带 Authorization 信息,直接访问某个 url 例如:/profile 还是未鉴权。

    有没有办法全局修改浏览器在当前网站中的 Authorization 头信息呢?

    33 条回复    2025-09-28 16:44:44 +08:00
    hsfzxjy
        1
    hsfzxjy  
       2023-07-11 13:57:03 +08:00 via Android
    jwt 鉴权一般只用于 API 上吧,没有直接访问 API URL 的场景。实在不行 access token 放 cookie 里也能接受
    thinkershare
        2
    thinkershare  
       2023-07-11 13:59:43 +08:00
    Authorization HEADER 这种授权模式本来就不是为了 Page 模式设计的,它主要是为了服务/服务通讯和 SPA 设计的。
    没有办法全局设置浏览器的 Authorization, 一般如果的确需要使用顶级 GET 请求后端并附带权限,会再 query 字符串上添加 token=JwtToken 。如果你不是当页面,使用 cookie/session 模式才是更方便的。
    LandCruiser
        3
    LandCruiser  
       2023-07-11 14:00:06 +08:00
    我理解是请求接口才验证 jwt ,请求静态资源不校验 jwt 呀
    dreasky
        4
    dreasky  
       2023-07-11 14:02:58 +08:00
    封装一个全局请求方法带上 headers 参考 axios 全局拦截器
    baiheinet /td>
        5
    baiheinet  
       2023-07-11 14:04:01 +08:00
    后端模拟前端登录

    const template = `
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" cOntent="IE=edge">
    <meta name="viewport" cOntent="width=device-width, initial-scale=1.0">
    <title></title>
    </head>
    <body>
    <script>
    const channel = new BroadcastChannel('response');
    channel.postMessage(${JSON.stringify(params)})
    window.close();
    </script>
    </body>
    </html>
    `;

    ctx.body = template;
    victimsss
        6
    victimsss  
       2023-07-11 14:05:45 +08:00   1
    直接放在 cookies 注意同源问题就行了。
    sujin190
        7
    sujin190  
       2023-07-11 14:08:27 +08:00
    jwt 为啥不能放 cookie 里,瞎搞扯,服务端为啥不能同时兼容 Authorization header 和 cookie 传递,这种标准本来就是推荐而已,既然不好用那不是给自己挖坑么
    NessajCN
        8
    NessajCN  
       2023-07-11 14:10:46 +08:00   1
    jwt 当然是放 cookie 里啊....
    /login 验证完在 response 的 header 里加上 Set-Cookie: token=<jwtstring>
    flyqie
        9
    flyqie  
       2023-07-11 14:12:35 +08:00
    @sujin190 #7

    确实,怎么传 jwt 是看业务需求,jwt 只能保证内容不会被篡改。
    cnfczn
        10
    cnfczn  
    OP
       2023-07-11 14:14:44 +08:00
    感谢大家的回复, 如果确实要改成 jwt 的话,看来只能 header+cookie 了。
    StrangerA
        11
    StrangerA  
       2023-07-11 14:15:41 +08:00
    ```typescript
    import { BadRequestException, Injectable } from '@nestjs/common'
    import { ConfigService } from '@nestjs/config'
    import { Request } from 'express'
    import { Strategy } from 'passport-jwt'
    import { PassportStrategy } from'@nestjs/passport'
    import { IToken } from '@/user/auth/auth.decorator'

    @Injectable()
    export class AuthJwtStrategy extends PassportStrategy(Strategy, 'jwt') {
    constructor(configService: ConfigService) {
    super({
    jwtFromRequest: AuthJwtStrategy.fromCookieOrHeader,
    secretOrKey: configService.get('JWT_SECRET'),
    })
    }

    public static fromCookieOrHeader(req: Request): string {
    const authHeader = req.header('authorization')
    if (authHeader && authHeader.startsWith('Bearer ')) {
    return authHeader.substring(7, authHeader.length)
    }
    return req.cookies['access_token']
    }

    // eslint-disable-next-line class-methods-use-this
    public async validate(payload: IToken): Promise<IToken> {
    if (payload.type !== 'access_token') {
    throw new BadRequestException('token 类型无效')
    }
    return payload
    }
    }
    ```

    passport.js 里,passport-jwt 的 strategy 没有粗暴地从 header 取 authorization 字段而是暴露了 jwtFromRequest ,就是希望使用者可以灵活一点。
    BreadKiller
        12
    BreadKiller  
       2023-07-11 14:16:20 +08:00
    前端封装 HTTP 请求,如果使用 axios 之类的就更方便了
    estk
        13
    estk  
       2023-07-11 14:31:56 +08:00
    放那里都行,只要你后端能拿到
    但是标准用法是放 header
    walpurgis
        14
    walpurgis  
       2023-07-11 14:43:31 +08:00 via iPhone   1
    不放 cookie 里是因为可以省去 csrf 防护
    FrankAdler
        15
    FrankAdler  
       2023-07-11 14:46:02 +08:00
    我这边是 cookie 、header 、get 参数顺序检查,同时兼顾 App 、Api 、前端的情况
    jiangzm
        16
    jiangzm  
       2023-07-11 14:54:10 +08:00
    优先校验 header token ,没有的话看 referrer 为空+GET 则校验 cookie token

    接口请求不携带 cookie ,页面 GET 请求会自动携带 cookie
    Tyaqing
        17
    Tyaqing  
       2023-07-11 18:25:22 +08:00
    这个需要弄个中间件维持吧
    FrankFang128
        18
    FrankFang128  
       2023-07-11 19:13:00 +08:00
    cookie 有安全问题的,你要加 csrf_token 才行。
    retanoj
        19
    retanoj  
       2023-07-11 19:27:14 +08:00
    @walpurgis 其实 Cookie 在 SameSite 之后 CSRF 问题好了很多很多了
    LawlietZ
        20
    LawlietZ  
       2023-07-11 19:29:35 +08:00
    其实通用的就是登陆成功后把 token 放 cookie 里,大厂里通用的 sso 登陆也是,至于其他安全问题有专门的解决办法,比如 csrf
    LawlietZ
        21
    LawlietZ  
       2023-07-11 19:31:04 +08:00
    你说的 session 鉴权其实也是 node 框架把登录状态保存到了 cookie 里,然后服务端解析到之后又放在了 req 对象的 session 字段里方便后端处理。
    momocraft
        22
    momocraft  
       2023-07-11 19:38:28 +08:00
    HTML SSR 和 JWT 匹配不好

    JWT 是个有期限且期限内不方便撤销的 secret, 让页面地址包含 JWT 等于把这个 secret 存到用户浏览记录里
    不如不用 JWT
    lologame
        23
    lologame  
       2023-07-11 19:38:58 +08:00
    JSON Web Token (JWT) is a compact claims representation format
    intended for space constrained environments such as HTTP
    Authorization headers and URI query parameters.

    https://www.rfc-editor.org/rfc/rfc7519

    你的问题是如何传输 JWT 的问题,和 JWT 本身没有直接关系。
    lonisletend
        24
    lonisletend  
       2023-07-11 19:50:22 +08:00

    我请求用的是 axios, 可以这么封装, 登录之后写 setToken, 需要注意的是, setToken 里必须再设置一下 axios.defaults.headers.common['Authorization'], 否则登录之后紧接着的其他请求不会带着 token, 没查到为啥, 所以设置一下比较保险, 之后所有的使用 axios 的请求都会带着 token 了.
    lucifer1108
        25
    lucifer1108  
       2023-07-11 19:53:22 +08:00
    放 cookie 里
    kingjpa
        26
    kingjpa  
       2023-07-11 19:55:10 +08:00
    你就当写小程序就完了,小程序咋写 页面咋写,就是给请求多一层封装
    keyframes
        27
    keyframes  
       2023-07-11 20:00:03 +08:00
    按照标准的话,就自己封装请求器自己往头信息加。
    其实可以改一下服务端的逻辑,从 cookie 中取出来后再验证,放 cookie 是没问题的,可以利用浏览器机制自动把信息带上去。
    webcape233
        28
    webcape233  
       2023-07-11 20:06:35 +08:00 via iPhone
    月经贴啊,放 cookie 方便,只是怕伪造请求,但是加 https only 限制 domain 的话其实也防住了, 放 header 可以 axios 统一封装,上面的楼也说过了。
    ysc3839
        29
    ysc3839  
       2023-07-11 21:30:35 +08:00 via Android
    jwt 并没限制放哪,既可以放 Authorization 头里,也可以放 cookie 里
    layxy
        30
    layxy  
       2023-07-12 09:31:41 +08:00
    axios 请求前拦截把 token 加入到 header 或者直接放到 cookie 中也可以,本身这个用法没有强制约束,只要你前后端协调好就可以
    libook
        31
    libook  
       2023-07-13 17:37:14 +08:00
    Token 也可以放在 HTTP 的 response 头里,比如用 authorization 头来返回给客户端。
    客户端不管是浏览器页面还是 App ,都可以做一个统一的请求拦截器,负责管理 token ,在登陆成功后自动读取返回的 token 信息并存储在本地,拦截每一个发送的请求自动读取本地存储的 token 信息加在 authorization 头里,根据认证策略自动刷新即将过期的 token 。

    比如你前端页面使用 axios 发送请求的话,可以看一下 axios 的 interceptors 相关文档。
    chuck1in
        32
    chuck1in  
       2023-07-18 12:48:22 +08:00
    op 不一定非要走这个 w3c 标准的,只要用了 token 其实就算是 jwt 了。
    yb2313
        33
    yb2313  
       11 天前
    @lonisletend 直接存 local 中 js 能访问,怎么防 xss 攻击呢?还是说你存的是 refresh token ,过一会就过期了?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     919 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 20:08 PVG 04:08 LAX 13:08 JFK 16:08
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86