基于 session 和基于 token 的用户认证方式到底该如何选择? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
qurioust
V2EX    程序员

基于 session 和基于 token 的用户认证方式到底该如何选择?

  •  
  •   qurioust 2016-05-04 11:22:13 +08:00 33094 次点击
    这是一个创建于 3527 天前的主题,其中的信息可能已经有所发展或是发生改变。

    现在貌似大多数网站用户认证都是基于 session 的,即在服务端生成用户相关的 session 数据,而发给客户端 sesssion_id 存放到 cookie 中,这样用客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户认证。这种认证方式,可以更好的在服务端对会话进行控制,安全性比较高(session_id 随机),但是服务端需要存储 session 数据(如内存或数据库),这样无疑增加维护成本和减弱可扩展性(多台服务器)。 CSRF 攻击一般基于 cookie 。另外,如果是原生 app 使用这种服务接口,又因为没有浏览器 cookie 功能,所以接入会相对麻烦。

    基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用户验证后,服务端生成一个 token(hash 或 encrypt)发给客户端,客户端可以放到 cookie 或 localStorage 中,每次请求时在 Header 中带上 token ,服务端收到 token 通过验证后即可确认用户身份。这种方式相对 cookie 的认证方式就简单一些,服务端不用存储认证数据,易维护扩展性强, token 存在 localStorage 可避免 CSRF , web 和 app 应用这用接口都比较简单。不过这种方式在加密或解密的时候会有一些性能开销(好像也不是很大),有些对称加密存在安全隐患(aes cbc 字节翻转攻击)。

    假如现在我想做一个适用于 web 和 native app 的 api 服务,该如何选择认证方式?还有如果使用基于 token 的认证方式, token 的设计有没有什么比较好的解决方案?

    43 条回复    2018-04-11 14:32:38 +08:00
    murmur
        1
    murmur  
       2016-05-04 11:26:25 +08:00
    这两个实际上不冲突 就算是基于 token 也可以在登录的时候访问你所有的子系统把上面的 session 激活了 oauth 一样可以 session
    想不泄露只能 https 传输 session id 都被偷了那就没办法了
    以前 ecshop 的 session 是绑了 ip 的 pc 端无所谓 移动端可能会有问题 我不知道跨信号区时 ip 会不会变
    还有见过的 神级办公室 一个办公室 2 个接入商(大概是电信联通双接入?) 但是策略没配置好 自己没事切 ip 玩
    neutrino
        2
    neutrino  
       2016-05-04 11:26:53 +08:00
    qurioust
        3
    qurioust  
    OP
       2016-05-04 11:39:32 +08:00
    @neutrino 我理解的 oauth2.0 一般都是给第三方授权使用的,本站的用户授权直接登录就好了吧?
    shiji
        4
    shiji  
       2016-05-04 11:43:28 +08:00
    外加:
    http://security.stackexchange.com/questions/19676/token-based-authentication-securing-the-token

    不过有 jwt 这种现成的,还是直接拿来用更安全一些
    SmiteChow
        5
    SmiteChow  
       2016-05-04 11:55:38 +08:00
    为什么说 token 不需要存储?
    jimrok
        6
    jimrok  
       2016-05-04 12:03:50 +08:00
    token 的方式更适合 api 使用,原理上和 session 使用的 cookie 没有太大的差别,但 token 更加简单, token 可以从后端应用发放。而 session 的 cookie 必须在 web 层产生和发放。
    hxsf
        7
    hxsf  
       2016-05-04 12:18:34 +08:00 via Android
    token 你也要存的 原理和 session+cookies 一样的。
    orvice
        8
    orvice  
       2016-05-04 12:18:43 +08:00
    @SmiteChow 如果用 jwt 不需要存。

    如果写的接口 app 也用就用 token 吧,原理其实搜差不多。
    chaegumi
        9
    chaegumi  
       2016-05-04 12:24:59 +08:00
    最近刚搞好了 jasig cas
    tabris17
        10
    tabris17  
       2016-05-04 12:25:43 +08:00
    这两个有本质区别么?
    bobuick
        11
    bobuick  
       2016-05-04 12:28:53 +08:00
    jwt.io JWT 这个方式很不错的呢。 简单易用
    qurioust
        12
    qurioust  
    OP
       2016-05-04 14:15:12 +08:00
    @neutrino @shiji @bobuick jwt 确实是基于 token 认证的一种比较好的实现方法,而且是行业标准 rfc7519 ,可以通过解码确认用户身份,这个我之前不知道,学习了。
    我看到的 token 实现方式有多种,主要参考了这个: http://security.stackexchange.com/questions/81756/session-authentication-vs-token-authentication
    Niphor
        13
    Niphor  
       2016-05-04 14:21:41 +08:00
    想起最近 搭个 node 渲染服务,后台 java 同学非要来 token 认证来取数...

    我是蛋疼的不要不要的,同一内网机子,还来 设备认证 + token 认证 + csrf...
    guotie
        14
    guotie  
       2016-05-04 14:31:48 +08:00
    用 jwt , token 不还是要保存?
    CodingWorker
        15
    CodingWorker  
       2016-05-04 14:37:57 +08:00
    学到了
    realpg
        16
    realpg  
    PRO
       2016-05-04 14:39:00 +08:00
    为啥只有我觉得 token 和 session 就是一回事……
    token 不就是 sessionid 么……
    julyclyde
        17
    julyclyde  
       2016-05-04 14:43:17 +08:00
    @realpg session 一般是指有状态的。 token 只是认证身份
    GavinHao
        18
    GavinHao  
       2016-05-04 14:47:39 +08:00 via Android
    jwt 的服务端实现是无状态的,在服务器端不需要保存 session 的,对于客户端而言倒类似于 session ID ,但不是去服务端找对应 session ,而是解码后校验。如有理解错误请指正。
    realpg
        19
    realpg  
    PRO
       2016-05-04 14:49:22 +08:00
    @julyclyde
    未必。而且 session 未必是 cookie 承载。现在已经没有人禁用 cookies 了,所以大家忘记了当年 session 的基本约定俗成规范是啥样的了。
    说 token 是 session 的一种都不为过。不就是 sessiOnid=xxx 变成了 token=xxx 么

    如果你写 java 版客户端,比如类似 android 客户端,使用 session 比使用 token 更省事,我指的 cookie 实现的 session , httpclient 之类直接外挂个储存的 cookiejar ,代码比 token 处处需要动传输数据结构更干净,而且可以实现 HEAD ,这对于 post/get 字串的方式来说,对于高性能系统优势明显
    julyclyde
        20
    julyclyde  
       2016-05-04 14:55:33 +08:00
    @realpg 我也没说 session 是 cookie 承载啊,你这不是竖个靶子自己打么; java 派似乎更偏好 querystring 承载。

    状态的“内容”是保存在服务器端的,提供服务器端使用 session 数组读出内容的能力。 token 是 不承诺 提供这个的
    jhdxr
        21
    jhdxr  
       2016-05-04 14:55:42 +08:00
    @jimrok 可是 session 不一定非得基于 cookie 的(当然时至今日大家大多使用 cookie ),例如各种 WAP 版一般直接使用 query string 来储存 session id
    softgoto
        22
    softgoto  
       2016-05-04 15:02:04 +08:00
    如果在基于 Token 的用户认证机制场景下,攻击者在客户端通过抓包工具获取到 Token 后,在 Token 的有效期内是不是就可以随便玩。

    如果换成 https 是不是就能解决这个问题
    GavinHao
        23
    GavinHao  
       2016-05-04 15:05:11 +08:00 via Android
    @softgoto 如果 session ID 被获取了不是一样被玩吗?
    fredcc
        24
    fredcc  
       2016-05-04 15:18:24 +08:00
    @softgoto 传输层安全不是 token 关心的问题啊
    zorrox
        25
    zorrox  
       2016-05-04 15:35:25 +08:00
    这两个都不是同一个层次的。。。
    orvice
        26
    orvice  
       2016-05-05 00:34:48 +08:00
    @softgoto oauth2/token 很依赖 https


    @GavinHao 考虑被截取没有什么意义,推荐下知乎这个问题 http://zhihu.com/question/20274730/answer/57877844
    owt5008137
        27
    owt5008137  
       2016-05-05 09:44:50 +08:00 via Android
    如果要深究的话, session id 不就是 session 的 token 吗?也就是 session 自带服务端缓存而已。

    如果你只提供 API ,不需要长时间的交互(类似 OAuth 那样),那就不要用 session 缓存,否则就直接用 session 呗。其实 OAuth 更多考虑的是大量客户端时候的安全问题和性能问题,所以如果没必要的话,简单最好
    flyingghost
        28
    flyingghost  
       2016-05-05 10:21:59 +08:00   1
    说 token 等于 sessionid 的真是误人子弟。
    虽然确实都是“客户端记录,每次访问携带”,但 token 很容易设计为自包含的,也就是说,后端不需要记录什么东西,每次一个无状态请求,每次解密验证,每次当场得出合法 /非法的结论。这一切判断依据,除了固化在 CS 两端的一些逻辑之外,整个信息是自包含的。这才是真正的无状态。
    而 sessionid ,一般都是一段随机字符串,需要到后端去检索 id 的有效性。万一服务器重启导致内存里的 session 没了呢?万一 redis 服务器挂了呢?

    方案 A :我发给你一张身份证,但只是一张写着身份证号码的纸片。你每次来办事,我去后台查一下你的 id 是不是有效。
    方案 B :我发给你一张加密的身份证,以后你只要出示这张卡片,我就知道你一定是自己人。
    就这么个差别。
    qurioust
        29
    qurioust  
    OP
       2016-05-05 10:36:56 +08:00
    @flyingghost 同意!我觉得这个才是基于 session 和 token 两种认证的最大区别,你这里说的更加清楚。那些说 token 也要在服务端存储的其实讲的还是 session 的思想,并没有理解这种区别。
    Jakesoft
        30
    Jakesoft  
       2016-05-05 10:38:25 +08:00
    session 不是 20 分钟过期吗?如果今天登陆明天怎么能做到自动登录?小白求解答
    julyclyde
        31
    julyclyde  
       2016-05-05 10:56:40 +08:00
    @flyingghost 你这个是不对的。对于 oauth2 场景, token 生成之后、有效期以内,如果修改了 password ,则要求 oauth2 服务器吊销已经生成的 token ,即提前使其过期。如果是自包含就不能完成这个功能了
    flyingghost
        32
    flyingghost  
       2016-05-05 11:19:40 +08:00
    @julyclyde 我的意思是“ token 很容易设计成自包含”,不是“所有的 token 都自包含”。主要针对的也是 lz 说的场景范围。
    OAuth 的 token 确实如你所说,以及还有很多设计都可以冠以“ token ”的名字,不是吗?: )
    qurioust
        33
    qurioust  
    OP
       2016-05-05 20:07:21 +08:00
    @julyclyde 服务器可以吊销 token ,是不是意味着要以用户 id 作为 key 来存储,这样是不是就没法实现一个账户多处登录?
    julyclyde
        34
    julyclyde  
       2016-05-05 20:56:47 +08:00
    @qurioust user 对 token 应该是一对多的关系
    julyclyde
        35
    julyclyde  
       2016-05-05 20:57:25 +08:00
    @flyingghost 如果你把“还有很多设计都可以冠以“ token ”的名字”这种话都说出来,那就没啥讨论的基础了……
    qurioust
        36
    qurioust  
    OP
       2016-05-06 00:03:51 +08:00
    @julyclyde user 对 token 是一对多关系并且不是自包含的,那么 token 可以是一个随机字符串并作为 key , user id 作为 value ,服务端把这种 key-value 关系存储下来。这样子可以实现一对多关系,并且也不是自包含的。但是这样子服务端吊销某个用户的 token 该如何实现?因为一般缓存总不能根据 value 去查询。不太明白,还望讲解一些。或者不应该是这种 key-value 的实现方式?
    julyclyde
        37
    julyclyde  
       2016-05-06 15:46:33 +08:00
    @qurioust 放数据库里?我印象中常见的 oauth2 provider 都提供“列出已签发的 token ”并提供单独吊销功能的
    qurioust
        38
    qurioust  
    OP
       2016-05-06 17:55:18 +08:00
    @julyclyde 觉得这样要再单独存储一层关系吧,比如 user id 对应的 token 列表。当要吊销的时候再根据 user id 找到下面的所有 token ,把他们从服务端清除。
    qurioust
        39
    qurioust  
    OP
       2016-05-06 18:05:56 +08:00
    @julyclyde

    如果直接使用数据库存储 token 的话那会方便吊销 token ,但是这种把不该持久化的东西持久化,总觉的不太好吧。

    我看到一个修改密码后吊销 token 的解决方案: http://stackoverflow.com/questions/28759590/best-practices-to-invalidate-jwt-while-changing-passwords-and-logout-in-node-js
    自包含的这种 token 实现吊销功能也是要单独再存储一个类似 InvalidTokenDB 的关系,觉得这个可以用 redis 来存储,方便处理过期 token 。
    julyclyde
        40
    julyclyde  
       2016-05-07 09:48:39 +08:00
    @qurioust 关系显然要存的,形式可以灵活
    token 也不算“不该持久化”的东西吧?有效期有时长达半年呢
    markocen
        41
    markocen  
       2016-05-07 09:51:16 +08:00
    token 的优点是 web server 上不需要保存用户的信息,更容易做分布式或者 web farm
    chaegumi
        42
    chaegumi  
       2016-08-16 09:55:43 +08:00
    我是搞 php 的,我看不懂官网的 jwt 示例

    ```java

    import org.pac4j.http.profile.HttpProfile;
    import org.pac4j.jwt.profile.JwtGenerator;
    ...
    JwtGenerator<HttpProfile> g = new JwtGenerator<>("<SIGNING_SECRET>", "<ENCRYPTION_SECRET>");
    HttpProfile profile = new HttpProfile();
    profile.setId("<PRINCIPAL_ID>");
    final String token = g.generate(profile);
    System.out.println(token);

    ```

    那几个尖括号里边的内容,能帮我举例子吗?
    xuxueli
        43
    xuxueli  
       2018-04-11 14:32:38 +08:00
    基于 sesssion 常用于 web 接入(登陆凭证存储在 cookie 中);基于 Token 常用于 APP 接入(登陆凭证需要主动存储,如 sqlite 中);

    可以了解下 XXL-SSO 啊,支持两种接入方式:

    http://www.xuxueli.com/xxl-sso/
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1020 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 18:43 PVG 02:43 LAX 10:43 JFK 13:43
    Do have faith in what you're doing.
    ubao msn 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