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

2016-05-04 11:22:13 +08:00
 qurioust

现在貌似大多数网站用户认证都是基于 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 的设计有没有什么比较好的解决方案?

32177 次点击
所在节点    程序员
43 条回复
jhdxr
2016-05-04 14:55:42 +08:00
@jimrok 可是 session 不一定非得基于 cookie 的(当然时至今日大家大多使用 cookie ),例如各种 WAP 版一般直接使用 query string 来储存 session id
softgoto
2016-05-04 15:02:04 +08:00
如果在基于 Token 的用户认证机制场景下,攻击者在客户端通过抓包工具获取到 Token 后,在 Token 的有效期内是不是就可以随便玩。

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


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

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

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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/276207

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX