V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
qurioust
V2EX  ›  程序员

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

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

    现在貌似大多数网站用户认证都是基于 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  
       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  
       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
    说 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   ·   实用小工具   ·   2881 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 12:59 · PVG 20:59 · LAX 04:59 · JFK 07:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.