什么是OAuth
OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而 不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容。
适用的场景
- 知乎登录qq账号例子
- 猴子都能看懂的图解
https://learnku.com/articles/20031
明确一些名词
- 用户
- 客户端(知乎)
- 资源服务端(qq)
简要介绍一些涉及到的参数
- Client_id : 资源服务端颁发给客户端的id。比如客户端是知乎,知乎希望用户可以不用注册,直接通过github获取用户名,头像等信息。那知乎就要去github申请一个client_id,让github看到这个client_id,就知道是从知乎过去的。
- Client_secret: 资源服务端颁发给客户端的密码。用于在关键步骤验证客户端的身份。
- Callback_url: 用于用户授权后的跳转,回到客户端的哪里。也用于接收Code。
- github作为资源服务端,知乎作为客户端申请client_id页面:
其他参数(可以先跳过)
- State : 非必须参数,但是推荐使用。用于防范csrf攻击。
- Code: 用户授权后,资源服务端发客户端的一个Code,用于客户端获取用户Token
- Token: 用户的认证token,仅在客户端和服务端之间传递,不暴露给用户。
- Scope: 想获得哪些权限
总流程图
举例流程
用户->知乎->qq授权页面->进行qq登录(手机/扫码/用户名)->用户同意授权->获取到code后重定向到知乎->将code传给知乎callback url告知要登录账号->知乎后台用code向qq索取用户token->用户在知乎登录成功
用户-> 知乎->qq授权页面
https://graph.qq.com/oauth2.0/show?which=Login&display=pc&scope=get_user_info,get_info,add_t,add_pic_t,get_other_info,get_fanslist,get_idollist,add_idol,add_share&state=64366461656664612d656333392d346636382d386332612d626163303364383836663761&redirect_uri=https://www.zhihu.com/oauth/callback/qqconn?action=login&from=&response_type=code&client_id=100490701
scope = get_user_info,get_info....
state = 64366461656664612...
redirect_url = https://www.zhihu.com/oauth/callback/...
response_type = code # 指定`授权码模式`(OAuth2有四种模式)
client_id = 100490701
进行qq登录(手机/扫码/用户名)->用户同意授权->获取到code后重定向到知乎
将code传给callback url告知要登录账号->知乎后台用code向qq索取用户token->用户在知乎登录成功
知乎后台用code向qq索取用户token
这一步用户是看不到的,具体的内容是:
- 知乎检查state,是否为一开始的(同一个用户的)state,防止csrf
- 拿着code和client_secret、client_id再去访问认证服务器,会返回一个token,这就是用户的最终token,拿着这个token,就可以去获取用户的头像,邮箱,用户名等资料了。
讨论
那么有几个问题
- 为什么非要用code去换一个token ,为什么不能在返回code的时候就把token返回来?
- 因为不仅要验证用户的身份,还要验证客户端的身份。获取token时还需要传client_secret就是用来验证客户端的身份。如果在之前的步骤里把client_secret带上,那就会把client_secret暴露给用户。说到这,其实第二种认证方式为简化认证。这种方式就是不对客户端进行验证,不需要传client_secret,然后直接在返回code的步骤中直接返回token。这种方式的缺点就是没有对客户端进行验证,而且token会暴露给用户。
- state参数有什么用?
- 防御csrf攻击。如果没有state参数,攻击者可以用自己的账号走完前几步的流程,获取到code。然后用精心设计的网页勾引受害者访问,假设此时受害者已经登陆过客户端网站但还没绑定服务端账号,攻击者的网页上让受害者去访问客户端的回调接口(此时没有state参数),当然,此时是用攻击者的code,返回的也是攻击者的token。至此,受害者的客户端账号就跟攻击者的服务端账号绑定在一起了。
- 那么如果有state参数,返回token时会验证state,如果当前用户所持state不是当初进入授权页面给的state,就会拒绝返回token。
简化模式
不对客户端进行验证,不需要传client_secret,然后直接在返回code的步骤中直接返回token。这种方式的缺点就是没有对客户端进行验证,而且token会暴露给用户。
密码模式(不推荐)
在客户端上直接进行服务端的账号密码登录,这种方式的缺点是会将用户的账号密码暴露给客户端。除非对客户端非常信任(自己的客户端),否则不要使用这种方式。
客户端模式
直接通过客户端的client_id和client_secret去获取token,完全不需要用户参与。是用于客户端去服务端拉取一组用户数据的。
参考资料
授权码模式图解
https://zhuanlan.zhihu.com/p/147513356
阮一峰博客(对state解释有误)
https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
csrf例子
https://www.zhihu.com/question/19781476
https://wooyun.js.org/drops/OAuth 2.0安全案例回顾.html