背景 单独把身份认证服务部署起来,在调用其他服务的时候,先通过身份认证。
因为Hydra版本出现了大的变动,即将2.0,改动巨大,本篇暂时以v1.10.6进行
本次我们来走一遍 OAuth 2.0 Authorize Code Flow
部署 启动Hydra服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 # 创建一个独立的网段 docker network create hydraguide # 拉取pg,当然也可使用mysql等其他数据库 docker pull postgres:9.6 # 拉取hydra docker pull oryd/hydra:v1.10.6 # 运行数据库(帐号:hydra 密码:secret) docker run \ --network hydraguide \ --name ory-hydra-example--postgres \ -e POSTGRES_USER=hydra \ -e POSTGRES_PASSWORD=secret \ -e POSTGRES_DB=hydra \ -d postgres:9.6 # 设置加密 export SECRETS_SYSTEM=$(export LC_CTYPE=C; cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) # 当然你也可以写死 export SECRETS_SYSTEM=SHARINGTOMMY123456789 # 创建临时的环境变量 DSN export DSN=postgres://hydra:secret@ory-hydra-example--postgres:5432/hydra?sslmode=disable # 初始化数据库 docker run -it --rm \ --network hydraguide \ oryd/hydra:v1.10.6 \ migrate sql --yes $DSN # 启动一个Hydra服务 docker run -d \ --name ory-hydra-example--hydra \ --network hydraguide \ -p 5444:4444 \ -p 5445:4445 \ -e SECRETS_SYSTEM=$SECRETS_SYSTEM \ -e DSN=$DSN \ -e URLS_SELF_ISSUER=http://localhost:5444/ \ -e URLS_CONSENT=http://localhost:9020/consent \ -e URLS_LOGIN=http://localhost:9020/login \ oryd/hydra:v1.10.6 serve all --dangerous-force-http # 验证(能看到正常启动日志) docker logs ory-hydra-example--hydra
说明:--network hydraguide
网络-p 5444:4444
public API http://localhost:5444/ -p 5445:4445
Hydra’s administrative API http://localhost:5445/ -e SECRETS_SYSTEM=$SECRETS_SYSTEM
加密变量-e DSN=$DSN
数据库变量-e URLS_SELF_ISSUER=http://localhost:5444/
是你的服务器地址-e URLS_CONSENT=http://localhost:9020/consent
是你前端用户同意授权地址-e URLS_LOGIN=http://localhost:9020/login
是前端用户登录地址-e URLS_LOGOUT
是你退出登录地址-e URLS_POST_LOGOUT_REDIRECT
是你退出登录成功后跳转到的地址-e TTL_ID_TOKEN
id_token 过期时间的设置单位 h m s,默认为1小时-e TTL_ACCESS_TOKEN
配置刷新令牌有效的时间。默认值为720h。设置为-1可使刷新令牌永不过期。-e TTL_REFRESH_TOKEN
配置标识令牌有效的时间。默认为1小时。
–dangerous-force-http 加了这句话就是不需要 https 如果你不加的话,URLS_SELF_ISSUER=https://localhost:4444/ 这里就要加s 加了https,https会有证书等问题。
登录/授权样例网站启动
该部分一般就是我们的前端登录授权页面,只是hydra提供了一个示例
1 2 3 4 5 6 7 8 docker pull oryd/hydra-login-consent-node:v1.10.6 docker run -d \ --name ory-hydra-example--consent \ -p 9020:3000 \ --network hydraguide \ -e HYDRA_ADMIN_URL=http://ory-hydra-example--hydra:4445 \ -e NODE_TLS_REJECT_UNAUTHORIZED=0 \ oryd/hydra-login-consent-node:v1.10.6
-p 9020:3000
暴露9020端口,这个端口就是URLS_CONSENT
和URLS_LOGIN
(URLS_CONSENT=http://localhost:9020/consent , URLS_LOGIN=http://localhost:9020/login ).HYDRA_ADMIN_URL=http://ory-hydra-example--hydra:4445
Hydra后台管理接口NODE_TLS_REJECT_UNAUTHORIZED=0
取消TLS校验
示例页面:
演示OAuth2.0流程 图解
请求与响应
授权请求 Authorization Request 浏览器打开
1 2 3 4 5 6 7 8 GET {认证终点} ?response_type=code // 必选项 &client_id={客户端的ID} // 必选项 &redirect_uri={重定向URI} // 可选项 &scope={申请的权限范围} // 可选项 &state={任意值} // 推荐 HTTP/1.1 HOST: {认证服务器}
授权响应 Authorization Response 获取code
1 2 3 4 HTTP/1.1 302 Found Location: {重定向URI} ?code={授权码} // 必填 &state={任意文字} // 如果授权请求中包含 state的话那就是必填
令牌请求 Access Token Request code换token
1 2 3 4 5 6 7 8 POST {令牌终点} HTTP/1.1 Host: {认证服务器} Content-Type: application/x-www-form-urlencoded grant_type=authorization_code // 必填 &code={授权码} // 必填 必须是认证服务器响应给的授权码 &redirect_uri={重定向URI} // 如果授权请求中包含 redirect_uri 那就是必填 &code_verifier={验证码} // 如果授权请求中包含 code_challenge 那就是必填
根据具体情况有可能是向客户端服务器进行请求,这时候请加上 Basic 认证(Authorization 头部)或者是 参数 client_id & client_secret
令牌响应 Access Token Response1 2 3 4 5 6 7 8 9 10 11 12 HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"{访问令牌}", // 必填 "token_type":"{令牌类型}", // 必填 "expires_in":{过期时间}, // 任意 "refresh_token":"{刷新令牌}", // 任意 "scope":"{授权范围}" // 如果请求和响应的授权范围不一致就必填 }
Hydra演示 创建一个facebook-photo-backup应用并获得id和secret 通过 Hydra CLI 命令创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 docker run --rm -it \ -e HYDRA_ADMIN_URL=http://ory-hydra-example--hydra:4445 \ --network hydraguide \ oryd/hydra:v1.10.6 \ clients create --skip-tls-verify \ --id facebook-photo-backup \ --secret some-secret \ --grant-types authorization_code,refresh_token,client_credentials,implicit \ --response-types token,code,id_token \ --scope openid,offline,photos.read \ --callbacks http://127.0.0.1:9010/callback You should not provide secrets using command line flags, the secret might leak to bash history and similar systems OAuth 2.0 Client ID: facebook-photo-backup
测试环境会有提示You should not provide secrets using command line flags, the secret might leak to bash history and similar systems
,忽略即可。 此时我们得到
1 2 Client ID: facebook-photo-backup Client Secret: some-secret
通过 rest api 创建 创建应用客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 POST http://localhost:5445/clients { "client_id": "facebook-photo-backup1", "client_secret": "some-secret", "token_endpoint_auth_method": "client_secret_basic", "redirect_uris": [ "http://127.0.0.1:9010/callback" ], "scope": "openid offline photos.read", "grant_types": [ "authorization_code", "refresh_token", "implicit", "client_credentials" ], "response_types": [ "code", "id_token", "token" ] }
执行一个 OAuth 2.0 授权流程 使用CLI简易 以下示例将执行一个 OAuth 2.0 授权流程。为简化此操作,Hydra CLI 提供了一个名为 hydra token user 的辅助命令。
1 2 3 4 5 6 7 8 9 10 11 docker run --rm -it \ --network hydraguide \ -p 9010:9010 \ oryd/hydra:v1.10.6 \ token user --skip-tls-verify \ --port 9010 \ --auth-url http://localhost:5444/oauth2/auth \ --token-url http://ory-hydra-example--hydra:4444/oauth2/token \ --client-id facebook-photo-backup \ --client-secret some-secret \ --scope openid,offline,photos.read
上面这个服务的过程请参考 下面的 使用Hydra api
,其实就是简化接口参数过程。
打开 http://127.0.0.1:9010/
这个时候我们使用Access Token去调用userinfo API,即可正常获取到用户信息.
1 2 3 4 5 6 7 curl -X GET \ http://localhost:5444/userinfo \ -H 'authorization: Bearer 6BboGKvjsyXG_ZFqX8NQboVi4v4JFqiLoKRZ2ex3QRI.fowTdOf3QqCojmmKTdm5MveKulsv-vcEkp2KdiROAMI' \ -H 'cache-control: no-cache' \ -H 'content-type: application/json' \ -H 'postman-token: fecb7032-db0a-bb1b-a61b-de22add7e5bc'
得到如下信息,sub就是用户的信息,为什么这里用户信息只有一个sub呢?因为他们实现ory-hydra-example–consent 的时候什么都没加进去,根据自己需要的信息加入sub就可以了。
1 2 3 4 5 6 7 8 { "subject": "foo@bar.com", "acr": "labo", "context": "<object>", "force_subject_identifier": "ex fugiat aliquip amet dolore", "remember": false, "remember_for": -4068005 }
使用Hydra api
整个过程最终目的就是要获取授权码,然后通过授权码去拿token,通过图可以看出获取授权码一共需要两个流程:Login
和Consent
下面我们就开始完整的演示一遍获取授权码的接口流程:
浏览器打开:http://localhost:5444/oauth2/auth?&client_id=facebook-photo-backup&response_type=code&scope=openid&state=nqvresaazswwbofkeztgnvfs
Hydra服务器会302重定向到你在创建的时候设定的前端登录地址:http://localhost:9020/login?login_challenge=xxxxxxxxxx
,带着login_challenge回来,这个东西就是下面接口需要的东西
这时候,我们前端应该进行身份验证的提交,并携带login_challenge到后端服务,后端接收到,通过对用户名和密码的校验后,请求acceptLoginRequest
登录请求
请求地址:http://localhost:5445/oauth2/auth/requests/login/accept?login_challenge=66cc8259bf0c4a3880e26c189968bbd6
请求方式:PUT
请求类型:application/json
请求参数:
1 2 3 4 5 6 7 8 { "subject": "foo@bar.com", "acr": "labo", "context": "<object>", "force_subject_identifier": "ex fugiat aliquip amet dolore", "remember": false, "remember_for": -4068005 }
请求成功返回:
1 2 3 { "redirect_to": "http://localhost:5444/oauth2/auth?client_id=facebook-photo-backup&login_verifier=289c0dbac2eb49e28eebc4b8f208b8c8&response_type=code&scope=openid&state=nqvresaazswwbofkeztgnvfs" }
我们再通过浏览器打开
Login认证流程就结束了,把请求成功返回的结果,继续丢到浏览器中,他会302重定向到你创建hydra的时候设定的consent
地址,并携带consent_challenge,,我们再通过前端提交把consent_challenge
的值传递给后端,让后端拿到下面去继续请求
认证请求
请求地址:http://localhost:5445/oauth2/auth/requests/consent/accept?consent_challenge=xxxxxx 请求方式:PUT
请求类型:application/json
请求参数: 说明下,session是可以写你想要放进id_token里面的东西,但是但是!请不要有中文,比如说:”name”:”小白”,这样Hydra也无法识别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { "grant_access_token_audience": [], "grant_scope": [ "openid" ], "handled_at": "2019-04-16T04:45:05.685Z", "remember": true, "remember_for": -72766940, "session": { "access_token": {}, "id_token": { "userId": "111" } } }
请求成功返回:
1 2 3 { "redirect_to": "http://localhost:4444/oauth2/auth?client_id=tommy&consent_verifier=373e5b86d4444fe2a78390df64efc9b1&prompt=&response_type=code&scope=openid&state=nqvresaazswwbofkeztgnvfs" }
把结果的验证接口地址继续放到浏览器中回车,hydra服务器会重定向到你创建应用时候设置的callback地址,并且后面带着code,如:http://127.0.0.1:9010/callback?code=xxxxxxxxx
,拿到这个code到下面的接口,就可以请求获取到Token了。
获取令牌、刷新令牌
请求地址:http://localhost:5444/oauth2/token
请求方式:POST
请求类型:application/x-www-form-urlencoded
请求参数
参数类型
参数说明
grant_type
字符串
授予类型,必填项
code
字符串
授权码
refresh_token
字符串
刷新令牌
client_id
字符串
客户端id,必填项
client-secret
字符串
客户端秘钥,必填项
redirect_uri
字符串
重定向uri
1 2 3 4 5 6 { "grant_type": "authorization_code", "client_id": "facebook-photo-backup", "redirect_uri": "http://localhost:9020/login" "code": "Qk4jf3dZ_DSkAAtlbS9pTilVFTRCeAYHdPpUN" }
返回
1 2 3 4 5 6 7 8 { "access_token": "nRNAO1g8LNMI2i_FJXQDoLxvHL7aLz4sILhWoL_de4w.EcGjSbAlCmsBSPlE_KtNk99AcFVaE_eqye0Sh41dXSk", "expires_in": 299, "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzpjNDk2N2ZkYi0yYTg0LTRhNjAtODRlZi02M2Q3MGFmNTRmNzIifQ.eyJhdF9oYXNoIjoia05wVkVkWGhTa2U4MmdvSk81SUFqUSIsImF1ZCI6WyJ6c3ctY2xvdWQtZGV2Il0sImF1dGhfdGltZSI6MTY1ODYyOTIxOSwiZXhwIjoxNjU4NjI5NTI2LCJpYXQiOjE2NTg2MjkyMjYsImlzcyI6Imh0dHBzOi8venN3LWh5ZHJhLmNvbS8iLCJqdGkiOiI1ZmIwMWM4NC1mNTZmLTQ0MGUtYjFmZC1iOTBjODNhY2ZkODIiLCJyYXQiOjE2NTg2MjkyMTIsInNpZCI6IjU2OWY2ZDczLTgzZDEtNDBlOS04YmMxLWFlMjFhY2QzOGYyZCIsInN1YiI6InpzdyJ9.OaAlvwFY84BU2_fF8RxsXK_ueoURmvIMl_Xa7xZ566laeZdJ8GyONzrlGDSLwNNhdKV8Mcl3U8aNoGZDb5w3DRca9C0rqaedo-r4zMrsAZ-YNUAXvuv_Ga-n_MDPA2FxLF0vz1Til48jkbWhQ0QmJnT_m6DvUo4veVjtbU6Ggbz2-rYO7adW2rp1gf4I_AwwUOjfBtQmqZRPNvQIkX-Md-bQfhqnGikMEkeoZdYuZP3ags6H1cm3E8eMLyJk4kGXGkMosSKLE8LFh1HrXYQfCDwCVpL1dy_-b0ZKyj20RVVdusBzdb97MV4QFeKleuyGIRBXHI0etW9EELOVjPWcz59tuE29uToSopiEArFpeCotsh4nllFxqtvqRM4zh5ZMjf6MIHpm74IW8nVlXdCVjBjzZp3Lg3th7iWEDrZm_9tZ1o0SmYYwf9IbjjttrIaBbph-iTm5aijN6WHrKM0HNOcrERrcK4REcSFKueL46-yHRKmOhwXNROJHZQu3mTpZRO8BnR3eWBsRuFmVGLt8BKi8s_fAR7AI__WN1y8rek2_34LnAVrh8CJQnzBAIB-9y6AeGH8a9t_tqxkJWeLa8ohXVH8VTceKkCMNm_7x9vvhhqlb8lyVau9ktvkIgoalyGRmBf66FZQkxpDFht0XiC7ZGq9IusI-fDSIcGRuJa8", "refresh_token": "emWnXUsZ43Sb9_eR0HyxirTltitFX0rvv2ouwRHdZ6U.4zKlg3iCdoHco02jryMHj432xzz0yQrh41zaQejp52M", "scope": "openid offline", "token_type": "bearer" }
最后我们拿着idToken去JWT解析看看效果可以看到,idToken解析出来你需要的信息。 至此我们就获取到了访问令牌access_token,前端可以将令牌缓存在cookie或session中,相应的后台也会缓存,后面前端调用其他服务时携带令牌调用接口,后台校验根据token来判断是否放行。
以上就是获取token的整个流程。
当然token也有过期的时候,下面说一下刷新令牌,接口和上面获取token是同一个http://localhost:5444/oauth2/token
,只是传参不同
1 2 3 4 5 6 7 { "grant_type": "refresh_token", "client_id": "facebook-photo-backup", "redirect_uri": "http://localhost:9020/login" // refresh_token是获取token时的refresh_token,不是access_token "refresh_token": "emWnXUsZ43Sb9_eR0HyxirTltitFX0rvv2ouwRHdZ6U.4zKlg3iCdoHco02jryMHj432xzz0yQrh41zaQejp52M" }
可用于用户登出
请求地址:http://localhost:5444/oauth2/revoke
请求方式:POST
请求类型:application/x-www-form-urlencoded
可用于获取用户标识或校验token是否存活
请求地址:http://localhost:5444/oauth2/introspect
请求方式:POST
请求类型:application/x-www-form-urlencoded
相关链接 Run Ory Hydra in Docker deprecate –dangerous-force-http flag [简易图解]『 OAuth2.0』 猴子都能懂的图解 [简易图解]『 OAuth2.0』 『进阶』 授权模式总结 微服务Token方案之ORY Hydra授权中心_Java实现