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

请教个 jwt 鉴权的问题

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

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

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

    32 条回复    2023-07-18 12:48:22 +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
        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 了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1577 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 147ms · UTC 16:59 · PVG 00:59 · LAX 08:59 · JFK 11:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.