From 6f2d9f672f3f81b38a4c843d2c2854abbfa54db4 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 26 Sep 2021 12:23:57 +0800 Subject: [PATCH 01/15] remove --- docs/CNAME | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/CNAME diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index 26f82a0..0000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -docs.oneauth.cn \ No newline at end of file From b231725a77979120d62764b4c813d7d289ac9087 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 26 Sep 2021 12:26:04 +0800 Subject: [PATCH 02/15] rm -ddd --- docs/_navbar.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 docs/_navbar.md diff --git a/docs/_navbar.md b/docs/_navbar.md deleted file mode 100644 index 1e0f91d..0000000 --- a/docs/_navbar.md +++ /dev/null @@ -1,3 +0,0 @@ - -* [主页](http://120.26.84.189) -* [现代身份构建指南](/modernidentity/) From 5f2752604f38c346665a2a381680ccca6f234484 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 26 Sep 2021 15:32:39 +0800 Subject: [PATCH 03/15] yes --- README.md | 5 ++++- docs/README.md | 4 ---- docs/_coverpage.md | 9 --------- docs/modernidentity/ChapterEight.md | 2 +- docs/modernidentity/ChapterEleven.md | 2 +- docs/modernidentity/ChapterNine.md | 2 +- docs/modernidentity/ChapterSeven.md | 2 +- docs/modernidentity/ChapterSix.md | 2 +- docs/modernidentity/ChapterTen.md | 2 +- docs/modernidentity/ChapterTwelve.md | 2 +- docs/modernidentity/README.md | 6 ------ docs/modernidentity/_sidebar.md | 16 ---------------- docs/pics/2-1-IdentityLifecycle.jpg | Bin 60752 -> 0 bytes docs/pics/5-1 API Security Model.png | Bin 45512 -> 0 bytes docs/pics/5-2 Level0.jpg | Bin 23428 -> 0 bytes docs/pics/5-2 Level1.jpg | Bin 29494 -> 0 bytes docs/pics/5-2 Level2.jpg | Bin 35225 -> 0 bytes docs/pics/logo.ico | Bin 3343 -> 0 bytes docs/pics/logo.png | Bin 5294 -> 0 bytes docs/pics/readme.md | 1 - 20 files changed, 11 insertions(+), 44 deletions(-) delete mode 100644 docs/README.md delete mode 100644 docs/_coverpage.md delete mode 100644 docs/modernidentity/README.md delete mode 100644 docs/modernidentity/_sidebar.md delete mode 100644 docs/pics/2-1-IdentityLifecycle.jpg delete mode 100644 docs/pics/5-1 API Security Model.png delete mode 100644 docs/pics/5-2 Level0.jpg delete mode 100644 docs/pics/5-2 Level1.jpg delete mode 100644 docs/pics/5-2 Level2.jpg delete mode 100644 docs/pics/logo.ico delete mode 100644 docs/pics/logo.png delete mode 100644 docs/pics/readme.md diff --git a/README.md b/README.md index d7004b2..1657ea4 100644 --- a/README.md +++ b/README.md @@ -1 +1,4 @@ -# - 一本关于如何做好身份管理的书 a new book +# 一本关于如何做好身份管理的书 + +# An awesome identity book talk about how to build a morden identity. + diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 875b870..0000000 --- a/docs/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# 这是一本关于如何做好身份管理的书 -> This book talk about how to build a morden identity. - -[开始阅读](/modernidentity/) diff --git a/docs/_coverpage.md b/docs/_coverpage.md deleted file mode 100644 index 27f4c65..0000000 --- a/docs/_coverpage.md +++ /dev/null @@ -1,9 +0,0 @@ - - -![logo](pics/logo.png) - -# OneAuth 3.5 -> 让身份更简单、更安全 - -[Get Started](/modernidentity/) -[GitHub](https://github.com/OneAuth2/OneAuth) diff --git a/docs/modernidentity/ChapterEight.md b/docs/modernidentity/ChapterEight.md index 03aa382..aad3e38 100644 --- a/docs/modernidentity/ChapterEight.md +++ b/docs/modernidentity/ChapterEight.md @@ -1,4 +1,4 @@ -# 深入学习SAML2.0 +# 第八章 深入学习SAML2.0 众所周知,安全断言标记语言(SAML)2.0提供了两个重要特性:跨域单点登录(SSO)和身份联合。SAML2.0已被许多企业环境采用,因为它使企业能够让员工、客户和合作伙伴使用的应用程序将用户身份验证委托给集中的企业身份提供者。这为企业提供了一个身份管理和控制中心。如果您正在为大型企业客户编写应用程序,他们可能希望您的应用支持使用SAML2.0进行身份验证[](https://wiki.oasis-open.org/security/FrontPage)。 在本章中,我们将概述SAML2.0,它的设计用来解决什么问题,以及SAML2.0中的跨域单点登录和身份联合特性。我们还将提供一个融合的解决方案,说明如何利用OIDC这样的较新协议,并且仍然有效地实现对SAML的支持。 diff --git a/docs/modernidentity/ChapterEleven.md b/docs/modernidentity/ChapterEleven.md index 5a93b20..8cdd44c 100644 --- a/docs/modernidentity/ChapterEleven.md +++ b/docs/modernidentity/ChapterEleven.md @@ -1,4 +1,4 @@ -# 单点登录 +# 第十一章 单点登录SSO ## 什么是SSO 单点登录 diff --git a/docs/modernidentity/ChapterNine.md b/docs/modernidentity/ChapterNine.md index e848134..17401f4 100644 --- a/docs/modernidentity/ChapterNine.md +++ b/docs/modernidentity/ChapterNine.md @@ -1,4 +1,4 @@ -# 授权与访问策略执行 +# 第九章 授权与访问策略执行 前面的第五章介绍了API访问的安全,以及第六章和第七章分别介绍了用户授权、OIDC的协议,那么二者如何结合,共同保障应用、API和数据的安全,本章节将介绍如何通过授权来保障API访问策略的执行。 diff --git a/docs/modernidentity/ChapterSeven.md b/docs/modernidentity/ChapterSeven.md index 19660c2..fc8840d 100644 --- a/docs/modernidentity/ChapterSeven.md +++ b/docs/modernidentity/ChapterSeven.md @@ -1,4 +1,4 @@ -# OpenID Connect +# 第七章 OpenID Connect 如前一章所述,OAuth2.0提供了一个框架,用于授权应用程序调用API,但不是为验证应用程序的用户而设计的。 因为互联网的应用越来越多,不同的应用之间需要共享身份,亟需一个大家公认的标准来进行身份的共享。同时,因为技术的演进(微服务、云原生),对于API的授权需求越来越多。OAuth2.0的一些实现为此添加了专有的附加内容,但是需要一个标准的解决方案。OpenID Connect(OIDC)[i](https://openid.net/connect/) 协议在Oauth2.0之上提供了一个身份服务层,旨在允许授权服务器对应用程序的用户进行身份验证,并以标准的方式返回结果。在本章中,我们将更详细地描述OIDC解决的问题和应用程序如何使用OIDC对用户进行身份验证。 diff --git a/docs/modernidentity/ChapterSix.md b/docs/modernidentity/ChapterSix.md index f6b2d3b..fa89773 100644 --- a/docs/modernidentity/ChapterSix.md +++ b/docs/modernidentity/ChapterSix.md @@ -1,4 +1,4 @@ -# 深入学习OAuth2.0 +# 第六章 深入学习OAuth2.0 为了解决在API中进行身份管理的需要,您必须建立在坚实的基础上。你需要在协议和标准上建立你的API安全基础设施,并且这些标准是已经通过同行评审,并被市场采用。长期以来,缺乏这样的标准这一直是大型组织希望采用RestfulAPI的主要障碍。 diff --git a/docs/modernidentity/ChapterTen.md b/docs/modernidentity/ChapterTen.md index f3a3b74..0691314 100644 --- a/docs/modernidentity/ChapterTen.md +++ b/docs/modernidentity/ChapterTen.md @@ -1,4 +1,4 @@ -# Sessions 会话 +# 第十章 Sessions 会话 用户在一段时间内与应用程序的交互称为会话。Session代表浏览器与服务器的一次会话过程,这个过程是连续的,也可以时断时续的。 diff --git a/docs/modernidentity/ChapterTwelve.md b/docs/modernidentity/ChapterTwelve.md index 27fccf3..4801ef9 100644 --- a/docs/modernidentity/ChapterTwelve.md +++ b/docs/modernidentity/ChapterTwelve.md @@ -1,4 +1,4 @@ -# 强身份认证 +# 第十二章 强身份认证 不同的身份验证方法的认证强度并不相同。静态密码被认为是一种相对较弱的身份验证机制。目前大多数互联网的应用仍然在采用静态密码的方式来进行认证,当然,有很多涉及到更多的用户数据、用户隐私的应用、企业的数据,都在引入更强的身份验证形式。在本章中,我们将讨论静态密码的问题,以及如何引入更强的身份验证形式用于多因素身份验证和逐步增强身份验证。 diff --git a/docs/modernidentity/README.md b/docs/modernidentity/README.md deleted file mode 100644 index ebb2c50..0000000 --- a/docs/modernidentity/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# 现代身份构建指南 - -> An awesome identity book. - - - diff --git a/docs/modernidentity/_sidebar.md b/docs/modernidentity/_sidebar.md deleted file mode 100644 index a621204..0000000 --- a/docs/modernidentity/_sidebar.md +++ /dev/null @@ -1,16 +0,0 @@ - - -* [现代身份构建指南](modernidentity/ "现代身份构建指南") - * [引言](modernidentity/Introduction.md "引言") - * [第一章 现代身份管理的挑战](modernidentity/ChapterOne.md ) - * [第二章 身份的生命周期](modernidentity/ChapterTwo.md ) - * [第三章 身份演进的历史](modernidentity/ChapterThree.md ) - * [第四章 身份的供给/生成](modernidentity/ChapterFour.md ) - * [第五章 API安全的成熟度模型](modernidentity/ChapterFive.md ) - * [第六章 深入学习Oauth2.0](modernidentity/ChapterSix.md ) - * [第七章 深入学习OIDC](modernidentity/ChapterSeven.md ) - * [第八章 深入学习SAML2.0](modernidentity/ChapterEight.md ) - * [第九章 授权与访问策略执行](modernidentity/ChapterNine.md ) - * [第十章 身份认证会话](modernidentity/ChapterTen.md ) - * [第十一章 SSO单点登录](modernidentity/ChapterEleven.md ) - * [第十二章 身份认证增强](modernidentity/ChapterTwelve.md ) diff --git a/docs/pics/2-1-IdentityLifecycle.jpg b/docs/pics/2-1-IdentityLifecycle.jpg deleted file mode 100644 index 768cd7a07c289d8d30025fc801f2f87d69b2bd2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60752 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 DIo=ED diff --git a/docs/pics/5-1 API Security Model.png b/docs/pics/5-1 API Security Model.png deleted file mode 100644 index 565c9c16ee1320b94877513f67a51ef35b648f23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45512 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 O2Sfg+aAMexFaHO6005)_ diff --git a/docs/pics/5-2 Level1.jpg b/docs/pics/5-2 Level1.jpg deleted file mode 100644 index 1c9eacedefdf69d44aa5f6fb7f1ccf15d2701c83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29494 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%0DkBF{DZK-XObD_M5-r4 z_GhaDosG-znf5Hv9qunKH)%`-y($oNfC|an z{%6Jzba`^>I1DR~4;5V}G+X4s^0bzs*MR2v@+A*pwg=m84NX%q1s+_>-h3;%47Mgh zuDdX(s9d~sK-+*3y=cjC5UL=zjQH?&>-*_f(@`HP!XBRYbItfsoH0@xpX&~}gufV| zb&hI72F||`(s+=|A+hyhE+|3%og?qUfZ}S)PJ0*Vhqcf?O)@qgZ09b@7gg?d>H7#5 z)}kfVnmVOE0W3>N6JX!T!g>bFefZ9a7|}azJCx^AyV=w|kN~HYriN)EQ zPXZh0L@LIwUZ;IE320jX5q}0zBaf`^812D+e)a`w`0uPIf4gn|6^nNIF*QdPY8%!Uka$v zdoy7Hca^*`f3|xb7#bTJfrNw`iK2JH6%`A^LAVV>zBSsqZ<{hhs>fH*JlL6sA_&Gw zJA-?tsL~(K3rq}nF6_p@+f0yx`xbQ6aai9t1nF!SbLHY!>&d`0)7C~^SpfHHjupB5 zH4pUjLQ*gOYa&S%%Pc7v(Gy-)^ZxZGrolirMGHnkfso?u{r&2ZKa69Y_f9IbwZ`$_ zo<_(*<7ObjD^)h6z*oVb_SCL~t3c!O)|pW1mjx8mR2$|IeRizz^~1r1?4E2MITufk zYQ$~Q!3w|A3vmHQ^mnHGa?SV$gOnHZ0S zJZXJCOY(Z_;MpXA+qFORkDD>(=@B`&e#*a|Vxy2vTNbKId=+`H&F^(b+9@PcdGciq0HoUG?-imWz*8miSm`!$^Lm#m!%mg5 zs`OKVRVMs_l)_JA8fxJKcCyyo_912Kkk&T%GZJLvbs1HVW|4EX?R1C=NBlX^9ACIPv=vj#_ewwUzW0ZNiyag|WGvCqXZGRWeWgDAG47b$(XNZbt6VB-Rpr@*k8X?G_K0N0&_h|hnN8`XGC}GyfkKuee zyhBXLYe_WeoT!P2u7H2^n>QEVlthCB0O0;_RP4X2c>ti%XcQ+A!P*O<2h?zqDLNl= ze5tHun1_!W=-VBsQCd$wa2|XR|G;#0 zW7@}Z^xn#v&q7fUEmgH{N$8bBTRiQPJ+8@zp4N(5r2$S^JSYOa*i)ZaQFdMl?^n)S zEX99zWg%R-J&|s@FI)f*hY8NG72G-p3pSS*;#L`||ZARyfhg z#Vw>7EnXeC!KgY?V8faS67klYQ+ZV?jp}#1leaz`80ba)?$d&OS=-X6x_FOX&X(Sh zVGFf$HNfcnr$@!nhvMH-|Apuax~Vf^HZ5CCMk0aPzi z1xd=%IiqQTF63B3CW?`#X5~K?$(>ES`H|K>2NfXhj_WeG79VrMebDH<<7l<|sMz{_8FMK=UVC164 z!E3^$8@d>C1JBwnGoz;Ps%sU4vA0c3sNq*m42iaAoLLWqxm_Nede_pozFK7t<(JxG zkYV&@!3Qf={jmOj?_+!kwQ6i{#-{|>+M9*Ug}+XX5ce~h8oBd+rn*M*JRjvpt81qPb4YP^e-C_YR67x8NWc+%>j-rLk6fu#@nx1{G@@cdmxEG$;*n! zL5?Xr;`1e6h*lFOx?9BCH=R`7SWa;}=D7V4jAxHI)`Ln@X9QQohW`TfW!(n)0lRO= zKYk8QwCn^twMA}ZBj7hlE@Cl+0nJ z-~Mjb|E>u$G9N$MjdZdFA&Sq!j@Wbm^tyHY?{`{4KI0n$-T(?`8LxrsXM-vWxONhV zw!c8&@LV#zGK6eU3a$tDeDV*j^Yfxl!@C?9P@(Ql8{-h<{ zoJt4i{}Qh&%>cq@>!4zMq5+^x#T!MeZ1_VQJ$}_@_ExXm`k@djf%>uMW`Rgd{Zb-7 z2*Z_is45uVEgpx^v~HRce$uXl9_QXva@Urb!UgB6tlcy78oCJ>g#t1u%MoRvaZuVB zlBf_YR$Bpye@tET{9!)CP>Kp<_X+ueVVy

l2pHFVWh`#mEaVZwM+2>-&8ZpQCll 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 K$fp;4Q~nQLzM!`N diff --git a/docs/pics/readme.md b/docs/pics/readme.md deleted file mode 100644 index 8b13789..0000000 --- a/docs/pics/readme.md +++ /dev/null @@ -1 +0,0 @@ - From 371ece60b5d8bf4b69f6e7cf9ab6496de1a5e917 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 26 Sep 2021 15:36:03 +0800 Subject: [PATCH 04/15] a --- docs/index.html | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 docs/index.html diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 5ffeaca..0000000 --- a/docs/index.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - OneAuth - - - - - - - -

- - - - - - - From 5ffa30c95154936a4aab86a82c5a2011a60cd58d Mon Sep 17 00:00:00 2001 From: OneAuth <60951446+OneAuth2@users.noreply.github.com> Date: Sun, 26 Sep 2021 15:41:13 +0800 Subject: [PATCH 05/15] Delete .nojekyll --- docs/.nojekyll | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/.nojekyll diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index 8b13789..0000000 --- a/docs/.nojekyll +++ /dev/null @@ -1 +0,0 @@ - From fa097e452802aa8ca197b05ba61495f5710200e2 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 26 Sep 2021 09:21:28 +0000 Subject: [PATCH 06/15] GitBook: [books] 30 pages modified --- README.md | 6 +- SUMMARY.md | 22 ++ docs/README.md | 2 + docs/modernidentity/README.md | 2 + .../{ChapterEight.md => chaptereight.md} | 79 +++---- .../{ChapterEleven.md => chaptereleven.md} | 8 +- .../{ChapterFive.md => chapterfive.md} | 21 +- .../{ChapterFour.md => chapterfour.md} | 185 +++++++-------- .../{ChapterNine.md => chapternine.md} | 41 +--- .../{ChapterOne.md => chapterone.md} | 42 +--- .../{ChapterSeven.md => chapterseven.md} | 219 +++++++----------- .../{ChapterSix.md => chaptersix.md} | 152 +++++------- .../{ChapterTen.md => chapterten.md} | 11 +- .../{ChapterThree.md => chapterthree.md} | 56 ++--- .../{ChapterTwelve.md => chaptertwelve.md} | 35 ++- .../{ChapterTwo.md => chaptertwo.md} | 20 +- .../introduction.md | 4 +- 17 files changed, 366 insertions(+), 539 deletions(-) create mode 100644 SUMMARY.md create mode 100644 docs/README.md create mode 100644 docs/modernidentity/README.md rename docs/modernidentity/{ChapterEight.md => chaptereight.md} (78%) rename docs/modernidentity/{ChapterEleven.md => chaptereleven.md} (87%) rename docs/modernidentity/{ChapterFive.md => chapterfive.md} (90%) rename docs/modernidentity/{ChapterFour.md => chapterfour.md} (77%) rename docs/modernidentity/{ChapterNine.md => chapternine.md} (90%) rename docs/modernidentity/{ChapterOne.md => chapterone.md} (94%) rename docs/modernidentity/{ChapterSeven.md => chapterseven.md} (62%) rename docs/modernidentity/{ChapterSix.md => chaptersix.md} (80%) rename docs/modernidentity/{ChapterTen.md => chapterten.md} (90%) rename docs/modernidentity/{ChapterThree.md => chapterthree.md} (81%) rename docs/modernidentity/{ChapterTwelve.md => chaptertwelve.md} (77%) rename docs/modernidentity/{ChapterTwo.md => chaptertwo.md} (99%) rename docs/modernidentity/Introduction.md => xian-dai-shen-fen-jian-she-zhi-nan/introduction.md (95%) diff --git a/README.md b/README.md index 1657ea4..ab1ed96 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# 一本关于如何做好身份管理的书 +# 现代身份建设指南 -# An awesome identity book talk about how to build a morden identity. +## 一本关于如何做好身份管理的书 + +## An awesome identity book talk about how to build a morden identity. diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..a969131 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,22 @@ +# Table of contents + +* [现代身份建设指南](README.md) +* [docs](docs/README.md) + * [modernidentity](docs/modernidentity/README.md) + * [第五章 API安全的成熟度模型](docs/modernidentity/chapterfive.md) + * [第九章 授权与访问策略执行](docs/modernidentity/chapternine.md) + * [第八章 深入学习SAML2.0](docs/modernidentity/chaptereight.md) + * [第七章 OpenID Connect](docs/modernidentity/chapterseven.md) + * [第二章 身份的生命周期](docs/modernidentity/chaptertwo.md) + * [第一章 现代身份的挑战](docs/modernidentity/chapterone.md) + * [第四章 身份的供给/生成](docs/modernidentity/chapterfour.md) + * [第六章 深入学习OAuth2.0](docs/modernidentity/chaptersix.md) + * [第十一章 单点登录SSO](docs/modernidentity/chaptereleven.md) + * [第三章 身份演进历史](docs/modernidentity/chapterthree.md) + * [第十章 Sessions 会话](docs/modernidentity/chapterten.md) + * [第十二章 强身份认证](docs/modernidentity/chaptertwelve.md) + +## 现代身份建设指南 + +* [引言](xian-dai-shen-fen-jian-she-zhi-nan/introduction.md) + diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..7dd802a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,2 @@ +# docs + diff --git a/docs/modernidentity/README.md b/docs/modernidentity/README.md new file mode 100644 index 0000000..30d96ea --- /dev/null +++ b/docs/modernidentity/README.md @@ -0,0 +1,2 @@ +# modernidentity + diff --git a/docs/modernidentity/ChapterEight.md b/docs/modernidentity/chaptereight.md similarity index 78% rename from docs/modernidentity/ChapterEight.md rename to docs/modernidentity/chaptereight.md index aad3e38..c65211b 100644 --- a/docs/modernidentity/ChapterEight.md +++ b/docs/modernidentity/chaptereight.md @@ -1,7 +1,6 @@ # 第八章 深入学习SAML2.0 -众所周知,安全断言标记语言(SAML)2.0提供了两个重要特性:跨域单点登录(SSO)和身份联合。SAML2.0已被许多企业环境采用,因为它使企业能够让员工、客户和合作伙伴使用的应用程序将用户身份验证委托给集中的企业身份提供者。这为企业提供了一个身份管理和控制中心。如果您正在为大型企业客户编写应用程序,他们可能希望您的应用支持使用SAML2.0进行身份验证[](https://wiki.oasis-open.org/security/FrontPage)。 -在本章中,我们将概述SAML2.0,它的设计用来解决什么问题,以及SAML2.0中的跨域单点登录和身份联合特性。我们还将提供一个融合的解决方案,说明如何利用OIDC这样的较新协议,并且仍然有效地实现对SAML的支持。 +众所周知,安全断言标记语言(SAML)2.0提供了两个重要特性:跨域单点登录(SSO)和身份联合。SAML2.0已被许多企业环境采用,因为它使企业能够让员工、客户和合作伙伴使用的应用程序将用户身份验证委托给集中的企业身份提供者。这为企业提供了一个身份管理和控制中心。如果您正在为大型企业客户编写应用程序,他们可能希望您的应用支持使用SAML2.0进行身份验证。 在本章中,我们将概述SAML2.0,它的设计用来解决什么问题,以及SAML2.0中的跨域单点登录和身份联合特性。我们还将提供一个融合的解决方案,说明如何利用OIDC这样的较新协议,并且仍然有效地实现对SAML的支持。 ## 解决的问题 @@ -21,7 +20,6 @@ SAML规范定义了以下术语: * SAML断言–一种基于XML的消息,包含有关主题的安全信息。 * SAML Profile–定义如何将SAML消息用于业务用例(如跨域单点登录)的规范。 * 身份提供者–身份提供者是一个服务器,它在跨域单点登录的上下文中发布有关已验证主题的SAML断言。 - * 服务提供者–服务提供者将身份验证委托给身份提供者,并依赖于身份提供者在跨域单点登录上下文中发出的SAML断言中有关已验证主题的信息。 * 信任关系–SAML服务提供者和SAML身份提供者之间的协议,其中服务提供者信任身份提供者发布的断言。 * SAML协议绑定–描述如何将SAML消息元素映射到标准通信协议(如HTTP)上,以便在服务提供者和身份提供者之间进行传输。实际上,SAML请求和响应消息通常通过HTTPS发送,使用HTTP重定向或HTTP-POST,分别使用HTTP重定向和HTTP-POST绑定。 @@ -36,45 +34,40 @@ SAML规范定义了以下术语: ### 应用SP发起的SSO -跨域单点登录的最简单形式如图8-1所示。在此示例中,用户从服务提供商(SP)(应用程序)开始,因此称为“SP启动”流(该图描述了该场景,其中用户在身份提供者处没有现有的身份验证会话,因此必须进行身份验证)。 +跨域单点登录的最简单形式如图8-1所示。在此示例中,用户从服务提供商(SP)(应用程序)开始,因此称为“SP启动”流\(该图描述了该场景,其中用户在身份提供者处没有现有的身份验证会话,因此必须进行身份验证)。 ![8-1 SAML SP-Initiated.jpg](https://i.loli.net/2021/07/19/3n1IJDpqwYVevmM.jpg) -
8-1 SP发起的SSO
+8-1 SP发起的SSO 1. 用户向应用(Servie Provider)发起访问请求 -2. 应用向身份提供者(Identity provider)发出一个SAML Request请求,该请求通过浏览器进行重定向。 -3. 身份提供者(Identity provider)接下来将向用户发起身份验证。 +2. 应用向身份提供者\(Identity provider\)发出一个SAML Request请求,该请求通过浏览器进行重定向。 +3. 身份提供者\(Identity provider\)接下来将向用户发起身份验证。 4. 用户通过输入凭据进行身份验证。 -5. 身份提供者(Identity provider)使用包含SAML身份验证断言的SAML响应,通过用户的浏览器重定向回服务提供者(SP)。响应被发送到服务提供者的断言使用者服务(Assertion Consumer Service-ACS)的URL。 +5. 身份提供者\(Identity provider\)使用包含SAML身份验证断言的SAML响应,通过用户的浏览器重定向回服务提供者(SP)。响应被发送到服务提供者的断言使用者服务(Assertion Consumer Service-ACS)的URL。 6. 服务提供者(SP)使用并验证SAML响应,如果通过验证,响应用户的原始请求。 - - -### 身份服务IdP发起的SSO +### 身份服务IdP发起的SSO 图8-1显示了从服务提供者(应用程序)开始与用户的交互序列。这被称为应用发起的单点登录,因为用户在服务提供商(SP)发起交互。SAML还定义了另一个流程,称为“IdP initiated”-身份服务发起的SSO,其中用户从身份提供者(IdP)开始,如图8-2所示。在这种情况下,身份提供者使用SAML响应消息将用户的浏览器重定向到服务提供者,而服务提供者没有发送任何身份验证请求。在某些企业环境中,用户可以通过企业门户访问应用程序,从而可以使用这种流程。 -这种场景如下描述:当用户最初访问公司门户时,他们将被重定向到公司身份提供程序以登录。登录后,用户将返回到门户,并在门户上看到应用程序链接的菜单/图标。单击其中一个链接将用户重定向到身份提供程序,并将应用程序URL作为参数。身份提供程序检测到用户已经有一个经过身份验证的会话,并使用SAML响应消息将用户的浏览器重定向到应用程序,与SP initiated的情况相同。 -事实上,IdP发起的SSO,并不一定是需要门户,但是选择了使用门户是IdP发起SSO的一种常见方式。 +这种场景如下描述:当用户最初访问公司门户时,他们将被重定向到公司身份提供程序以登录。登录后,用户将返回到门户,并在门户上看到应用程序链接的菜单/图标。单击其中一个链接将用户重定向到身份提供程序,并将应用程序URL作为参数。身份提供程序检测到用户已经有一个经过身份验证的会话,并使用SAML响应消息将用户的浏览器重定向到应用程序,与SP initiated的情况相同。 事实上,IdP发起的SSO,并不一定是需要门户,但是选择了使用门户是IdP发起SSO的一种常见方式。 IdP发起的带有门户的流在企业中很有用,因为它提供了单点登录,并确保用户访问每个应用程序的正确URL,从而降低了用户被钓鱼的风险。IdP启动的流程如图7-2所示。(此图假设用户没有现有的身份验证会话,登录门户实际上是个SP发起SSO的流程,后半部分,用户访问SP是IdP发起SSO的流程。) ![8-2 SAML IdP-Initiated.jpg](https://i.loli.net/2021/07/19/1xYnSFjgClaWVKz.jpg) -
8-2 IdP发起的SSO
+8-2 IdP发起的SSO 1. 用户向门户发起访问请求 -2. 门户向身份提供者(Identity provider)发出一个SAML Request请求,该请求通过浏览器进行重定向。 -3. 身份提供者(Identity provider)接下来将向用户发起身份验证。 +2. 门户向身份提供者\(Identity provider\)发出一个SAML Request请求,该请求通过浏览器进行重定向。 +3. 身份提供者\(Identity provider\)接下来将向用户发起身份验证。 4. 用户通过输入凭据进行身份验证。 -5. 身份提供者(Identity provider)使用包含SAML身份验证断言的SAML响应,通过用户的浏览器重定向回门户。响应被发送到门户的断言使用者服务(Assertion Consumer Service-ACS)的URL。如果该断言通过门户服务的验证,门户展示用户可以访问的应用列表。 +5. 身份提供者\(Identity provider\)使用包含SAML身份验证断言的SAML响应,通过用户的浏览器重定向回门户。响应被发送到门户的断言使用者服务(Assertion Consumer Service-ACS)的URL。如果该断言通过门户服务的验证,门户展示用户可以访问的应用列表。 6. 用户点击要访问的应用的链接,该链接将通过浏览器重定向到身份服务者,同时将本次用户需要访问的应用的URL附带在参数中。 7. 身份服务者检查用户的Session是否还存在,如果存在,IdP使用包含SAML身份验证断言的SAML响应,通过用户的浏览器重定向回服务提供者(SP)。响应被发送到服务提供者的断言使用者服务(Assertion Consumer Service-ACS)的URL。 8. 服务提供者(SP)使用并验证SAML响应,如果通过验证,向用户展示该应用的页面。 - - ### 身份联邦 通过SAML,身份联合建立了在服务提供者(应用程序)和身份提供者之间使用的一致的标识符,以指向同一(用户)主体。这使得服务提供商能够将用户的认证委托给身份提供商,并接收具有身份声明的认证断言来判别用户。 @@ -83,7 +76,7 @@ IdP发起的带有门户的流在企业中很有用,因为它提供了单点 ![8-3 Identity Federation.jpg](https://i.loli.net/2021/07/19/fCneOhQEjwpis7k.jpg) -
8-3 身份联邦
+ 8-3 身份联邦 身份提供者的管理员向 Application 1和Application2的管理员提供身份服务有关其环境的元数据,二者使用元数据在Application1/2和身份提供者之间设置联合认证的配置。身份提供者将会向其配置的每个服务提供者发送断言,这些断言包含服务提供者(应用程序)的适当标识符和属性。 @@ -93,8 +86,6 @@ IdP发起的带有门户的流在企业中很有用,因为它提供了单点 服务提供者的身份和身份提供者之间的关系可以以不同的方式设置。在实践中,用户的电子邮件通常在服务提供商和身份提供商处被用作用户的标识符,但是这可能是有问题的,因为用户可能需要更改其电子邮件地址,并且它可能与隐私要求相冲突。可以在请求中动态请求特定标识符属性的使用,或者可以将身份提供者配置为向服务提供者发送特定标识符。身份提供者和服务提供者还可以使用用户的不透明内部标识符交换信息,该标识符在每侧映射到用户的概要文件。为每个联合体使用唯一标识符有利于隐私保护,并防止用户活动的关联,但在实践中并不常见,该方法需要在双方交换元数据并配置其基础结构以在服务提供者和身份提供者之间建立关系时设置。 - - ## 中间代理 身份验证中间代理的主要目的是为了支持多种身份验证协议和机制。有的客户希望他们的用户使用公司SAML身份提供者的认证服务。 @@ -105,35 +96,31 @@ IdP发起的带有门户的流在企业中很有用,因为它提供了单点 ![8-4-IDP Broker.jpg](https://i.loli.net/2021/07/19/VoqrRnTc1GymUFC.jpg) - - -
8-4 IdP中间代理
+8-4 IdP中间代理 ## 配置说明 表8-1和8-2列出了通常需要在服务提供者(应用程序)和身份提供者处配置的参数及其含义。 -| 参数 | 描述 | -| ------------------ | ------------------------------------------------------------ | -| SSO URL | 身份提供者的单点登录URL的入口,应用向该URL请求身份验证。 | -| Certificate | 来自身份提供者的证书。用于验证来自身份提供程序的SAML响应/断言上的签名,同时也可在应用发送加密请求时使用。某些提供程序允许两种用途使用不同的证书。 | -| Protocol binding | 指定发送请求时使用何种协议进行绑定。典型的使用HTTP-Redirect,或者 HTTP-POST 用于直接请求。 | -| Request signing | 用于指示是否对SAML身份验证请求进行数字签名,如果是,通过哪个签名算法。建议签名。 | -| Request encryption | 是否对SAML身份验证请求进行加密。 | - -
8-1 SAML认证 常用SP配置
+| 参数 | 描述 | +| :--- | :--- | +| SSO URL | 身份提供者的单点登录URL的入口,应用向该URL请求身份验证。 | +| Certificate | 来自身份提供者的证书。用于验证来自身份提供程序的SAML响应/断言上的签名,同时也可在应用发送加密请求时使用。某些提供程序允许两种用途使用不同的证书。 | +| Protocol binding | 指定发送请求时使用何种协议进行绑定。典型的使用HTTP-Redirect,或者 HTTP-POST 用于直接请求。 | +| Request signing | 用于指示是否对SAML身份验证请求进行数字签名,如果是,通过哪个签名算法。建议签名。 | +| Request encryption | 是否对SAML身份验证请求进行加密。 | -| 参数 | 描述 | -| ------------------ | ------------------------------------------------------------ | -| ACS URL | 身份提供者的单点登录URL的入口,应用向该URL请求身份验证。 | -| Certificate | 来自身份提供者的证书。用于验证来自身份提供程序的SAML响应/断言上的签名,同时也可在应用发送加密请求时使用。某些提供程序允许两种用途使用不同的证书。 | -| Protocol binding | 指定发送响应时使用何种协议进行绑定。典型的使用 HTTP-POST 。 | -| Request signing | 用于指示是否对SAML身份验证响应进行数字签名,如果是,通过哪个签名算法。签名是强制性的。 | -| Request encryption | 是否对SAML身份验证响应进行加密。 | - -
8-2 SAML认证 常用IDP配置
+8-1 SAML认证 常用SP配置 +| 参数 | 描述 | +| :--- | :--- | +| ACS URL | 身份提供者的单点登录URL的入口,应用向该URL请求身份验证。 | +| Certificate | 来自身份提供者的证书。用于验证来自身份提供程序的SAML响应/断言上的签名,同时也可在应用发送加密请求时使用。某些提供程序允许两种用途使用不同的证书。 | +| Protocol binding | 指定发送响应时使用何种协议进行绑定。典型的使用 HTTP-POST 。 | +| Request signing | 用于指示是否对SAML身份验证响应进行数字签名,如果是,通过哪个签名算法。签名是强制性的。 | +| Request encryption | 是否对SAML身份验证响应进行加密。 | +8-2 SAML认证 常用IDP配置 ## 本章重点回顾 @@ -155,11 +142,7 @@ SAML2.0为web单点登录和身份联合提供了行业标准解决方案。 一旦解决了身份验证,就需要授权和策略执行来控制用户可以做什么,这将在下一章中介绍。 - - ## 参考: -i: https://wiki.oasis-open.org/security/FrontPage - - +i: [https://wiki.oasis-open.org/security/FrontPage](https://wiki.oasis-open.org/security/FrontPage) diff --git a/docs/modernidentity/ChapterEleven.md b/docs/modernidentity/chaptereleven.md similarity index 87% rename from docs/modernidentity/ChapterEleven.md rename to docs/modernidentity/chaptereleven.md index 8cdd44c..ca64988 100644 --- a/docs/modernidentity/ChapterEleven.md +++ b/docs/modernidentity/chaptereleven.md @@ -28,19 +28,17 @@ ### SSO会话时长 -应配置SSO会话的长度,通常以最大超时和空闲超时来指定。当然,应用程序具有不同的敏感性,可以适应具有不同需求的应用程序需求,做出不同的响应。如果使用OIDC的应用程序需要用户更频繁地进行主动身份验证,则身份验证请求中的“max_age”参数可用于指定自用户上次主动身份验证以来所允许的最长时间(以秒为单位),通常比身份提供程序会话的时长要短。如果身份验证请求中的“max_age”值小于用户上次身份验证后经过的时间,则使用此参数要求身份提供程序再次主动对用户进行身份验证。应用程序仍应检查ID令牌中的身份验证时间声明,以确保遵循了请求的“max_age”。 +应配置SSO会话的长度,通常以最大超时和空闲超时来指定。当然,应用程序具有不同的敏感性,可以适应具有不同需求的应用程序需求,做出不同的响应。如果使用OIDC的应用程序需要用户更频繁地进行主动身份验证,则身份验证请求中的“max\_age”参数可用于指定自用户上次主动身份验证以来所允许的最长时间(以秒为单位),通常比身份提供程序会话的时长要短。如果身份验证请求中的“max\_age”值小于用户上次身份验证后经过的时间,则使用此参数要求身份提供程序再次主动对用户进行身份验证。应用程序仍应检查ID令牌中的身份验证时间声明,以确保遵循了请求的“max\_age”。 对于非敏感的应用,应用也可通过使用更长的应用会话时长,即便是身份提供程序会话已经结束,用户在应用程序中也可以保持活动状态, ### 多个身份服务提供商 -在某些情况下,会存在配置多个身份提供程序的身份源的情况,这时需要使用到身份代理实现SSO,身份代理应确保每个身份提供程序的用户只能登录到他们的被授予访问的应用程序。 -例如,如果一家公司有一个身份验证代理,其中一个身份提供者为员工配置,另一个为合作伙伴配置,则该配置应确保合作伙伴无法访问仅针对员工的应用程序。 -该场景如图11-2所示。在此示例中,应用程序A只能由身份提供程序A认证的用户访问。应用程序B只能由身份提供程序B认证的用户访问。由登录到身份提供程序B的用户建立的SSO会话则不允许访问应用程序A。 +在某些情况下,会存在配置多个身份提供程序的身份源的情况,这时需要使用到身份代理实现SSO,身份代理应确保每个身份提供程序的用户只能登录到他们的被授予访问的应用程序。 例如,如果一家公司有一个身份验证代理,其中一个身份提供者为员工配置,另一个为合作伙伴配置,则该配置应确保合作伙伴无法访问仅针对员工的应用程序。 该场景如图11-2所示。在此示例中,应用程序A只能由身份提供程序A认证的用户访问。应用程序B只能由身份提供程序B认证的用户访问。由登录到身份提供程序B的用户建立的SSO会话则不允许访问应用程序A。 ![11-2 Multi IDP.png](https://i.loli.net/2021/09/08/Tg3KNLdhYPVFDue.png) -
11-2 IDP 代理
+11-2 IDP 代理 ### 认证机制 diff --git a/docs/modernidentity/ChapterFive.md b/docs/modernidentity/chapterfive.md similarity index 90% rename from docs/modernidentity/ChapterFive.md rename to docs/modernidentity/chapterfive.md index bbed505..6136277 100644 --- a/docs/modernidentity/ChapterFive.md +++ b/docs/modernidentity/chapterfive.md @@ -8,18 +8,16 @@ 事实上,API的安全的演进也并非一触而就,也经历了一系列的演进。参考Richardson RMM( API成熟度模型),该模型概述了Web API服务开发成熟度的四个层级。相对应的,从安全角度,构建了API的安全成熟度模型。在这个模型中,随着等级升高,安全性和信任度就越高。 -- Level 0: API 密钥和基础认证 ,API Keys and Basic Authentication - -- Level 1: 基于令牌的认证,Token-Based Authentication -- Level 2: 基于令牌的授权,Token-Based Authorization - -- Level 3: 基于声明的集中式信任,Centralized Trust Using Claims +* Level 0: API 密钥和基础认证 ,API Keys and Basic Authentication +* Level 1: 基于令牌的认证,Token-Based Authentication +* Level 2: 基于令牌的授权,Token-Based Authorization +* Level 3: 基于声明的集中式信任,Centralized Trust Using Claims ![5-1 API Security Model.png](https://i.loli.net/2021/07/16/mhWVXogISRsQ9ju.png) ### Level 0 API密钥和基础认证 -级别0的API使用基本身份验证或API密钥来验证API调用。这些值插入在API请求的URL的标头(Header)或正文(Body)中。大多数早期的API都采用这种方式。 +级别0的API使用基本身份验证或API密钥来验证API调用。这些值插入在API请求的URL的标头\(Header\)或正文\(Body\)中。大多数早期的API都采用这种方式。 例如,一个电子商务商店的应用,它根据用户购买情况对付款API进行API调用。它将身份验证以标头中的API密钥或基本身份验证的形式发送给应用程序,并将其传递给API。将用户的ID放在正文或URL中。在下面的示例中,有两类API:账单类和商品类。由于采用HTTP基本身份验证或API密钥方式,API仅对“商店应用”进行身份验证,因此“商店应用”必须向API传递用户数据。 @@ -47,7 +45,7 @@ 为了解决以上级别1的缺陷,在令牌的设计上,级别2在基于令牌认证的体系结构上,引入了授权机制。即,通过描述请求方的特权,询问请求方将被允许做什么?这一思想被应用到Oauth2.0中,作为一种广泛采用的授权标准。 -OAuth最大的亮点是在token中包含了作用范围(标准中定义为Scope),范围Scope可以在授权令牌中包含“指定的权限”,即作用范围可以指定用户权限。 这一优点在后续的协议中又得以进一步的规范,如后续的OpenID Connect定义了一组标准的[作用范围],即可以用来生成标准的身份参数。当然,开发者除了标准范围以外,也可以创建[自定义范围]用于自己开发的API。范围可以根据开发者需求包含更多和授权相关的数据,以用于后续的API的授权,这比“ if”语句的编码方式要更为友好和优雅。 +OAuth最大的亮点是在token中包含了作用范围(标准中定义为Scope),范围Scope可以在授权令牌中包含“指定的权限”,即作用范围可以指定用户权限。 这一优点在后续的协议中又得以进一步的规范,如后续的OpenID Connect定义了一组标准的\[作用范围\],即可以用来生成标准的身份参数。当然,开发者除了标准范围以外,也可以创建\[自定义范围\]用于自己开发的API。范围可以根据开发者需求包含更多和授权相关的数据,以用于后续的API的授权,这比“ if”语句的编码方式要更为友好和优雅。 这时,让我们再次回顾我们的电子商店的案例。现在,我们引入了通过范围进行授权,因此可以更加容易的实现公共网络的商店和后台可以具有不同的特权。比如,只有管理后台应用才能获取添加商品(AddProduct)的权限。这就有效的区分了不同应用的权限。 @@ -59,8 +57,6 @@ OAuth最大的亮点是在token中包含了作用范围(标准中定义为Scop 级别2的问题:级别2中的一个问题是系统面临着反编译的威胁。如果将身份的信息直接放在在API参数中,通过对于API的访问进行逆向,其中的逻辑错误很容易被发现和利用。 另外,级别2中引入了更高的系统复杂度,因为某些API请求参数可能会依赖其他API响应或其他条件。如果,一个API调用另一个API时失败了,或者,如果数据请求参数中包含了错误,怎么办?我们不能假设数据从一个API传递到下一个API总是正确的。这些现实问题可能会导致一系列的级联信任问题,这种“意大利面条式的信任”,有可能让系统变成一团糟。 - - ## Level 3: 基于声明的集中式信任,Centralized Trust Using Claims 通过对以上各级的问题的总结,最终API的安全计划到第3级,可以认为是目前最完善的API安全思想和实践。这种实践引入了使用Claims和签名的JSON Web令牌(JWT)进行集中式信任。 @@ -83,16 +79,15 @@ OAuth最大的亮点是在token中包含了作用范围(标准中定义为Scop 这种方法通过信任令牌的发行者,而不是信任参数本身的方式,有效的解决了各级信任问题。从而达成集中式的授权机制。 - - ## 本章重点回顾 信任是主观的东西。在设计基于API的安全系统时,我们应该信任密钥、令牌,密码,机器或用户本身吗?答案要比大多数的API设计人员认为的要复杂,但这对于保护您的平台整体至关重要。 - 本质上,API安全的顶峰是信任声明,而不是属性。如果没有考虑更高级的安全性,如果GitHub存储库中存在重要的密钥,API很容易受到攻击。因此,API提供者必须做出更明智的安全决策,以保护整个平台的完整性。 +本质上,API安全的顶峰是信任声明,而不是属性。如果没有考虑更高级的安全性,如果GitHub存储库中存在重要的密钥,API很容易受到攻击。因此,API提供者必须做出更明智的安全决策,以保护整个平台的完整性。 正如API安全成熟度模型逐步演进所显示的那样,高度成熟的API只信任很少的源,这些演化的API被信任的令牌的发行者逐渐成为独立的服务。这不能保证100%的真实性,但最接近于验证请求方的身份。通过标准逐渐固化信任标准过程,可以消除系统开发者在身份授权的自定义代码上浪费的精力。 事实上,在网络安全领域,很少鼓励你发明自己的授权规则。为了使集中信任发挥作用,授权系统需要使用稳定的协议。就像街道交通规则一样,身份系统也遵循通用协议。需要它们自己的共享开放标准,这些协议就是OAuth和OpenID Connect。利用这些标准,应用程序可以在JWTs中共享安全的断言数据以进行验证。 下一章将详细的介绍OAuth。 + diff --git a/docs/modernidentity/ChapterFour.md b/docs/modernidentity/chapterfour.md similarity index 77% rename from docs/modernidentity/ChapterFour.md rename to docs/modernidentity/chapterfour.md index 60e90d3..7286bd8 100644 --- a/docs/modernidentity/ChapterFour.md +++ b/docs/modernidentity/chapterfour.md @@ -6,14 +6,11 @@ 对于应用程序开发人员来说,身份供给阶段包括获取用户并为他们创建帐户和身份配置文件。通常,最简单的理解是让用户注册应用程序的帐户,但这不是唯一的选择。正对不同的场景,身份供给的常用方式包括: -- 用户通过注册填写信息登记表单来创建新身份。 - -- 特定用户通过注册邀请来填写账户注册表单。 - -- 从以前存在的用户存储库转化而来。 - -- 管理员或自动化进程创建用户身份信息。 -- 利用具有现有用户标识存储库的身份服务。 +* 用户通过注册填写信息登记表单来创建新身份。 +* 特定用户通过注册邀请来填写账户注册表单。 +* 从以前存在的用户存储库转化而来。 +* 管理员或自动化进程创建用户身份信息。 +* 利用具有现有用户标识存储库的身份服务。 这些方法不是相互排斥的;在某些情况下,多种方法的结合可能最有效。我们将更详细地描述每种方法,以及每种方法的优缺点。 @@ -25,13 +22,13 @@ 表 4-1总结了使用登记表的一些优点和缺点。 -| 优点 | 缺点 | -| :--------------------------------- | :----------------------------------------------------------- | +| 优点 | 缺点 | +| :--- | :--- | | 能够收集其他地方不存在的用户属性。 | 一些潜在的新的用户不愿意填写注册信息,可能会影响新用户的注册。 | -| 可以控制用户注册体验。 | 承担存储登录凭据相关的责任。 | -| 通过自助服务实现可扩展性。 | | +| 可以控制用户注册体验。 | 承担存储登录凭据相关的责任。 | +| 通过自助服务实现可扩展性。 | | -
表4-1 用户自注册
+ 表4-1 用户自注册 #### 渐进式注册 @@ -57,14 +54,14 @@ 邀请注册方法的一些优点和缺点如表所示 4-2. -| 优点 | 缺点 | -| ---------------------------------- | ------------------------------------------------------------ | +| 优点 | 缺点 | +| :--- | :--- | | 能够收集其他地方不存在的用户属性。 | 一些潜在的新的用户不愿意填写注册信息,可能会影响新用户的注册 | -| 控制用户注册体验。 | 承担存储登录凭据相关的责任。 | -| 通过自助服务实现可扩展性。 | 需要额外设计发出邀请的工作。 | -| 防止黑客和机器人注册。 | 对于邀请链接和控制其访问的额外开发工作。 | +| 控制用户注册体验。 | 承担存储登录凭据相关的责任。 | +| 通过自助服务实现可扩展性。 | 需要额外设计发出邀请的工作。 | +| 防止黑客和机器人注册。 | 对于邀请链接和控制其访问的额外开发工作。 | -
表4-2 邀请注册
+ 表4-2 邀请注册 ### 身份迁移 @@ -74,17 +71,17 @@ 散列密码有不同的算法,传递给散列算法的输入也不同,例如salt和迭代计数。因此,在一个系统中散列的密码不一定能被另一个系统导入和使用。如果两个不同的系统使用不同的散列算法或同一算法的不同输入,则不可能将散列密码从一个系统移动到另一个系统并使其可供新系统使用。在这种情况下,有几种解决办法考虑迁移身份 一个新的系统。 -#### 支持遗留系统哈希算法 +#### 支持遗留系统哈希算法 一种解决方案是更新新系统以支持遗留系统使用的哈希算法。这需要在新系统中实现遗留系统的哈希算法,以及确定每个帐户使用哪个哈希算法的方法。这将允许将所有身份数据和散列密码从旧系统移动到新系统,而无需用户重置密码。表 4-3总结了支持传统哈希算法的一些优点和缺点。 -| 优点 | 缺点 | -| ------------------------ | ------------------------------ | -| 无需重新设置密码。 | 实施遗留哈希算法。 | -| 以可用状态转移所有帐户。 | 承担存储登录凭据相关的责任。 | -| | 继承与遗留哈希算法相关的缺点。 | +| 优点 | 缺点 | +| :--- | :--- | +| 无需重新设置密码。 | 实施遗留哈希算法。 | +| 以可用状态转移所有帐户。 | 承担存储登录凭据相关的责任。 | +| | 继承与遗留哈希算法相关的缺点。 | -
表4-3 全部迁移
+ 表4-3 全部迁移 #### 批量密码重置 @@ -92,43 +89,39 @@ 表 4-4总结了批量转移用户的一些优点和缺点。 -| 优点 | 缺点 | -| -------------------------------------------------- | ------------------------------------------------------------ | -| 一次传输所有用户。 | 需要转移所有帐户,即使是非活动帐户,除非在转移过程中过滤掉它们。 | -| 支持立即关闭旧用户存储库。 | 要求所有用户通过帐户恢复设置新密码,除非新系统可以支持旧的哈希密码。 | +| 优点 | 缺点 | +| :--- | :--- | +| 一次传输所有用户。 | 需要转移所有帐户,即使是非活动帐户,除非在转移过程中过滤掉它们。 | +| 支持立即关闭旧用户存储库。 | 要求所有用户通过帐户恢复设置新密码,除非新系统可以支持旧的哈希密码。 | | 在登录时无需去就系统验证,降低验证凭据带来的延迟。 | 需要缜密的规划,否则如果迁移出现问题且没有备份计划,可能会导致大范围的中断。 | -| 传输身份的代码可以独立于应用程序代码。 | 承担与存储登录凭据相关的责任。 | +| 传输身份的代码可以独立于应用程序代码。 | 承担与存储登录凭据相关的责任。 | -
表4-4 批量迁移但不包含密码
+ 表4-4 批量迁移但不包含密码 #### 逐步迁移用户 -批量密码重置之外,可以改进的是使用热更新的机制,在用户登录时逐渐迁移身份。这需要一种登录机制,该机制提示用户输入凭据,根据旧存储库对其进行验证,如果验证通过,则从旧存储库检索身份信息,并将其和输入的凭据存储在新存储库中。在对遗留系统进行验证之后,用户输入的密码由新系统使用其哈希算法进行哈希运算,并存储在用户的新帐户中。这种方式将仅迁移登录并要求新系统验证的用户(事实上,新系统在迁移之前仍然访问旧系统并验证输入的凭证和检索用户身份信息)。这对用户来说更方便,因为不需要重置密码,但这意味着在迁移标识之前,遗留系统必须保持可操作性。此解决方案不会转移非活动帐户 (未登录的用户)。 +批量密码重置之外,可以改进的是使用热更新的机制,在用户登录时逐渐迁移身份。这需要一种登录机制,该机制提示用户输入凭据,根据旧存储库对其进行验证,如果验证通过,则从旧存储库检索身份信息,并将其和输入的凭据存储在新存储库中。在对遗留系统进行验证之后,用户输入的密码由新系统使用其哈希算法进行哈希运算,并存储在用户的新帐户中。这种方式将仅迁移登录并要求新系统验证的用户(事实上,新系统在迁移之前仍然访问旧系统并验证输入的凭证和检索用户身份信息)。这对用户来说更方便,因为不需要重置密码,但这意味着在迁移标识之前,遗留系统必须保持可操作性。此解决方案不会转移非活动帐户 \(未登录的用户)。 使用渐进迁移方法,一部分用户可能在迁移期间并没有登录,因此无法迁移其身份信息。您可以设置迁移的截止日期,并决定如何处理在该日期之前尚未迁移的任何标识。一种可能是宣布未迁移的帐户处于非活动状态,并放弃它们。一种常见的方法是使用批量移动非活动帐户(不包含散列的密码),如上一小节描述,这样就可以解除旧系统的运行。如果不迁移所有剩余的标识,则应考虑保留未迁移标识的帐户标识符,以防止将来新帐户使用它们。 对于身份尚未迁移的用户,可以使用新系统输入电子邮件并获得密码重置令牌或链接。用户将确认收到电子邮件,并提示输入新密码。新系统将为新系统中的用户创建一个帐户,其中包含从旧系统中检索到的具有匹配电子邮件地址的帐户的信息。活动标识的逐步迁移,再加上剩余身份的大量迁移和凭证重置,为活跃用户提供了一种良好的用户体验,同时不放弃不经常的用户。表 4-5总结了逐步迁移的一些优点和缺点。 +| 优点 | 缺点 | +| :--- | :--- | +| 不活跃的帐户可以剔除。 | 要求可以从新应用程序的身份验证机制访问旧标识存储。在传输足够的标识之前,旧标识存储必须保持可访问性。 | +| 无需重置密码 \(对于在迁移期间登录的用户)。 | 在逐步迁移的过程中,必须保持迁移机制。 | +| 通过逐步迁移身份分散停机风险(无大面积故障风险)。 | 因为身份数据需要从遗留系统读取,用户在迁移开始后的首次登录可能会有一些延迟。 | +| 可以支持在渐进迁移期间继续使用以前的注册机制或使用遗留标识存储库的应用程序。 | 需要额外的开发实现 | +| | 承担存储登录凭据相关的责任。 | - -| 优点 | 缺点 | -| ------------------------------------------------------------ | ------------------------------------------------------------ | -| 不活跃的帐户可以剔除。 | 要求可以从新应用程序的身份验证机制访问旧标识存储。在传输足够的标识之前,旧标识存储必须保持可访问性。 | -| 无需重置密码 (对于在迁移期间登录的用户)。 | 在逐步迁移的过程中,必须保持迁移机制。 | -| 通过逐步迁移身份分散停机风险(无大面积故障风险)。 | 因为身份数据需要从遗留系统读取,用户在迁移开始后的首次登录可能会有一些延迟。 | -| 可以支持在渐进迁移期间继续使用以前的注册机制或使用遗留标识存储库的应用程序。 | 需要额外的开发实现 | -| | 承担存储登录凭据相关的责任。 | - -
表4-5 逐步迁移
+ 表4-5 逐步迁移 ### 管理员创建 创建帐户和身份需要考虑的另一个解决方案是让管理员或自动化流程来创建它们。应对这种情况的最佳方法应该考虑到: * 组织的规模 - * 用户被创建的频率 - * 是否涉及到跨域的操作 下面的部分提供了该解决方案的几个变体: @@ -145,30 +138,28 @@ 在几种情况下,可能需要跨域进行帐户设置。这可能发生在: -- 在外部SaaS(软件即服务)应用程序中维护员工帐户 -- 在企业标识存储库或应用程序中维护合作伙伴帐户 - -- 在面向业务的应用程序中维护业务客户用户帐户 -- 在合作大学系统中维护客座教授或学生帐户 +* 在外部SaaS(软件即服务)应用程序中维护员工帐户 +* 在企业标识存储库或应用程序中维护合作伙伴帐户 +* 在面向业务的应用程序中维护业务客户用户帐户 +* 在合作大学系统中维护客座教授或学生帐户 理想情况下,现代身份验证协议会在登录时将用户档案文件属性传递给身份验证令牌中的应用程序,但是如果需要,可能仍然需要跨域提供或同步身份信息 -- 应用程序没有设计从身份验证令牌中提取身份信息的。 -- 身份资料文件信息太大,无法在身份验证令牌中传输。 - -- 用户登录不够频繁,无法及时充分更新用户信息。 +* 应用程序没有设计从身份验证令牌中提取身份信息的。 +* 身份资料文件信息太大,无法在身份验证令牌中传输。 +* 用户登录不够频繁,无法及时充分更新用户信息。 在需要时,跨域提供帐户和身份信息通常仍使用专有解决方案,如使用创建于2015年的行业标准协议SCIM(跨域身份管理系统),旨在提供一种更标准的方法,将身份信息从一个域发送到另一个域并进行更新。 表4-6显示了管理员创建帐户的一些优点和缺点。 -| 优点 | 缺点 | -| -------------------------------------------- | ------------------------------------------------ | -| 用户无需填写注册表。 | 如果不是自动化的话,会很费时。 | -| 管理员可以分配权限,以便帐户以所需权限启动。 | 需要注意确保只有用户知道所创建帐户的密码。 | -| 可通过工作流或身份设置软件实现自动化。 | 如果存储在本地,则需承担存储登录凭据相关的责任。 | +| 优点 | 缺点 | +| :--- | :--- | +| 用户无需填写注册表。 | 如果不是自动化的话,会很费时。 | +| 管理员可以分配权限,以便帐户以所需权限启动。 | 需要注意确保只有用户知道所创建帐户的密码。 | +| 可通过工作流或身份设置软件实现自动化。 | 如果存储在本地,则需承担存储登录凭据相关的责任。 | -
表4-6 管理员创建
+ 表4-6 管理员创建 ### 利用现有外部的身份服务 @@ -176,38 +167,35 @@ 利用现有身份提供者服务中的帐户可能意味着减少用户在注册表单中输入的数据。这通常也意味着用户不需要设置另一个密码。如果您不必实现登录表单或帐户恢复机制,因为所有用户都通过身份提供程序服务进行身份验证,那么这可能会减少开发工作。如果您的基础结构中没有存储用户密码,那么还可以在一定程度上降低风险。如果身份提供程序服务不包含应用程序所需的有关用户的所有属性,则您可以随时在以后收集其他数据。 4-7总结了使用外部身份服务的一些优点和缺点。 -| 优点 | 缺点 | -| ------------------------------------------------------------ | ------------------------------------------------------------ | -| 更好的用户体验,如果它减少了注册所需的数据。 | 您可能需要收集身份提供程序服务无法提供的其他配置文件信息。 | -| 如果经常使用身份提供程序帐户,用户更容易记住密码。 | 您需要评估外部标识服务的服务和可用性级别,以确保它满足您的需求。 | +| 优点 | 缺点 | +| :--- | :--- | +| 更好的用户体验,如果它减少了注册所需的数据。 | 您可能需要收集身份提供程序服务无法提供的其他配置文件信息。 | +| 如果经常使用身份提供程序帐户,用户更容易记住密码。 | 您需要评估外部标识服务的服务和可用性级别,以确保它满足您的需求。 | | 如果所有用户都通过身份提供程序服务进行身份验证,则可能不必实现登录窗体或帐户恢复机制。 | 可能需要为要使用的每个身份提供者服务进行额外的开发或配置工作。 | -| 如果不存储用户密码,风险更小。 | 出现问题时,可能需要与其他组织协作解决问题。 | +| 如果不存储用户密码,风险更小。 | 出现问题时,可能需要与其他组织协作解决问题。 | -
表4-7 使用外部身份服务
+ 表4-7 使用外部身份服务 除了现有的身份提供者服务之外,企业也可以设置自己的新身份提供者服务,以供应用程序使用。如果您选择了该路径,那么可以使用许多云服务(IDaaS)来简化任务,并且可以使用以前的任何设置选项来向新的身份提供者服务填充用户。 - - ## 4.2外部身份服务的类型 如果您选择利用外部标识服务,那么重要的是要考虑由服务发布的标识的强度以及提供者对特定环境的适用性和可用性。身份的强度是决定对身份的信任程度的一个因素,有几个因素会影响身份的强度: -- 用于确定身份的信息的验证 -- 防止他人伪造或使用虚假的身份 - -- 身份的颁发者对某一特定领域具有权威性 +* 用于确定身份的信息的验证 +* 防止他人伪造或使用虚假的身份 +* 身份的颁发者对某一特定领域具有权威性 表4-8提供了身份与弱身份特征的比较 -| 强身份 | 弱身份 | -| ---------------------------------------- | -------------------------------- | -| 链接到一个真实的、能够对其行为负责的人。 | 匿名的,不能和真人联系。 | -| 在帐户颁发过程中身份属性可以被验证。 | 能够提供验证的属性很少。 | -| 由特定环境下被公认为权威的实体发布。 | 由缺乏公认权威的实体发布。 | -| 包含防止伪造或未经授权使用的机制。 | 缺乏防止伪造或未经授权使用的机制 | +| 强身份 | 弱身份 | +| :--- | :--- | +| 链接到一个真实的、能够对其行为负责的人。 | 匿名的,不能和真人联系。 | +| 在帐户颁发过程中身份属性可以被验证。 | 能够提供验证的属性很少。 | +| 由特定环境下被公认为权威的实体发布。 | 由缺乏公认权威的实体发布。 | +| 包含防止伪造或未经授权使用的机制。 | 缺乏防止伪造或未经授权使用的机制 | -
表4-8 强弱身份对比
+ 表4-8 强弱身份对比 身份的强度取决于发行人的可信度、身份数据的验证、发行和分发身份背后的实践,比如:发行人与信任发行人身份信息的任何实体之间的协议,无论是隐含的还是明确的。下一节将提供示例。 @@ -229,8 +217,6 @@ 移动网络运营商的身份是一个特殊的载体,用户在申请移动电话号码的时候,会经过严格的实名验证。并且签发后它与一张Sim卡绑定,这张卡具有异乎寻常的安全加固措施,极难被复制和伪造。因而,持有这样卡片的人,几乎等同于持有政府颁发的身份。但这里仅仅是几乎,因为一个人可以拥有多张手机卡,仍然存在他把这张卡主观的或被动的交由他人使用的情况。但对于绝大多数线上的业务,没有那么严格要求消费者身份的场景下,拥有该手机卡使用权的用户,等同于注册该卡片人的身份。 - - ## 4.3身份提供商选择 > 面向消费者 @@ -253,13 +239,13 @@ 表 4-9总结了不同场景中最常见的身份提供者类型。 -| 场景 | 可选择的身份服务商 | -| ----------------- | ------------------------------------------------------------ | -| B2C:面向消费者 | 社交登录:微信、微博、Github、Gmail等,
运营商身份服务
聚合身份服务商:Okta、OneAuth、Firebase、Cognito | -| B2E:面向员工 | 身份服务商:Okta 、OneAuth、AzureAD等 | -| B2B:面向合作伙伴 | 身份服务商:Okta、OneAuth、AzureAD
或其他支持OIDC、SAML的身份服务 | +| 场景 | 可选择的身份服务商 | +| :--- | :--- | +| B2C:面向消费者 | 社交登录:微信、微博、Github、Gmail等, 运营商身份服务 聚合身份服务商:Okta、OneAuth、Firebase、Cognito | +| B2E:面向员工 | 身份服务商:Okta 、OneAuth、AzureAD等 | +| B2B:面向合作伙伴 | 身份服务商:Okta、OneAuth、AzureAD 或其他支持OIDC、SAML的身份服务 | -
表4-9 常见外部身份
+ 表4-9 常见外部身份 ## 4.4选择一个验证身份的属性 @@ -275,17 +261,17 @@ > 手机号码作为标识 -手机号码作为全球唯一的标识,能够作为一种强身份标识,被面向消费者的应用广泛应用。使用手机号码,通过手机号码的验证码能够简化用户的注册流程。并且运营商针对手机端,提供了一种无密码登录的方式,大大降低了移动端用户认证的复杂度,提升了用户的体验。 但手机号码作为身份标识可能也面临一些问题,如用户可能会更换手机号码,但仍然期望保留账户。存储手机号码,涉及到用户的敏感信息,需要承担更多的责任。 +手机号码作为全球唯一的标识,能够作为一种强身份标识,被面向消费者的应用广泛应用。使用手机号码,通过手机号码的验证码能够简化用户的注册流程。并且运营商针对手机端,提供了一种无密码登录的方式,大大降低了移动端用户认证的复杂度,提升了用户的体验。 但手机号码作为身份标识可能也面临一些问题,如用户可能会更换手机号码,但仍然期望保留账户。存储手机号码,涉及到用户的敏感信息,需要承担更多的责任。 表 4-10列出了不同标识符的一些共同优点和缺点。 -| 方式 | 优点 | 缺点 | -| -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| 电子邮件 | 全球唯一标识
比用户名更容易记忆 | 用户可能需要变更邮件,但需要保留账户。
某些用户可能没有邮箱 | -| 用户名 | 没有暴露用户的身份信息
比邮件更短,更容易输入 | 只在某一应用里唯一,如果被占用,其他人无法再注册
无法进行两个系统的合并
用户拥有多个站点的用户名后,不容易记忆 | -| 手机号 | 全球唯一标识
比用户名更容易记忆
强身份标识,可以用于账号恢复 | 用户可能需要变更手机号,但需要保留账户。
认证过程可能带来一定的费用
涉及到用户的隐私,需要谨慎对待 | +| 方式 | 优点 | 缺点 | +| :--- | :--- | :--- | +| 电子邮件 | 全球唯一标识 比用户名更容易记忆 | 用户可能需要变更邮件,但需要保留账户。 某些用户可能没有邮箱 | +| 用户名 | 没有暴露用户的身份信息 比邮件更短,更容易输入 | 只在某一应用里唯一,如果被占用,其他人无法再注册 无法进行两个系统的合并 用户拥有多个站点的用户名后,不容易记忆 | +| 手机号 | 全球唯一标识 比用户名更容易记忆 强身份标识,可以用于账号恢复 | 用户可能需要变更手机号,但需要保留账户。 认证过程可能带来一定的费用 涉及到用户的隐私,需要谨慎对待 | -
表4-10 账户的标识符
+ 表4-10 账户的标识符 ### 对于标识符的建议 @@ -296,24 +282,18 @@ * 用于通知和账户恢复 * 用于应用内的行为标记,包括 * 将身份/帐户链接到应用内的操作 - * 在日志文件中记录用户活动 + * 在日志文件中记录用户活动 列表中的最后两个用于应用内帐户的实现,并且应使用唯一的内部帐户标识符,该标识符不受用户更改其电子邮件、电话号码或法定名称等用户属性变更的影响。此外,以下建议可以避免表4-10中列出的其他一些缺点 : * 避免暴露可能包含个人数据的标识符 - * 在日志文件中使用内部帐户标识符以避免直接暴露日志中的个人数据。 * 在申请记录中使用内部账户标识符。 * 允许用户指定用于屏幕/打印输出的显示名称,以保护隐私。 - * 登录、显示和通知的标识符/属性可以根据需要变更。 - * 允许为通知目的设置多个属性,如主电子邮件和辅助电子邮件,以防其中一个无法操作。 - * 允许长用户名带有特殊字符,并且用户可以更改,这将实现灵活性,包括 如果允许用户使用更容易记住的电子邮件,同时也允许其他用户使用其他值。 - - ### 验证关键属性 除了为不同的目的,使用不同的用户属性外。在影响安全和隐私的活动中,也需要对一些关键的属性进行验证,这包括验证电子邮件、手机号码等等。验证后的属性可以用于 @@ -328,8 +308,7 @@ 如果用户可以使用虚构的、未经验证的电子邮件地址注册,并且此属性用于授权,则一些授权会授予给这些恶意虚构的用户,让其拥有的本不应该让他们访问的一些资源的访问权限。另外,严格验证电子邮件地址还可以防止意外输入不正确的地址,以造成错误,让他人通过帐户恢复机制接管帐户,或导致将敏感信息传递给错误的收件人。 - - ## 4.5本章重点回顾 本章介绍了几种可用于为应用程序用户建立帐户的方法,包括自注册、渐进式登记、从其他地方转移用户、管理员创建和利用身份提供商服务。在选择账户的标识符时,您需要根据应用程序的敏感度和目标受众来考虑每个选项提供的标识的强度和适用性。一旦您知道如何创建用户,就可以开始实现身份验证和访问控制。现代应用程序通常是从API开始设计的,因此我们将在下一章从OAuth2.0开始,OAuth2.0是为保护API而设计。 + diff --git a/docs/modernidentity/ChapterNine.md b/docs/modernidentity/chapternine.md similarity index 90% rename from docs/modernidentity/ChapterNine.md rename to docs/modernidentity/chapternine.md index 17401f4..876abf7 100644 --- a/docs/modernidentity/ChapterNine.md +++ b/docs/modernidentity/chapternine.md @@ -14,8 +14,6 @@ ![9-1 AuthZ and Police Enforcement.jpg](https://i.loli.net/2021/07/20/XDnzPT7K6ptyY48.jpg) - - ## 控制级别 授权和访问策略强制可分别指定和应用于不同的级别: @@ -24,8 +22,6 @@ * 级别2–实体可以在应用程序或API中使用哪些功能 * 级别3—实体可以访问或操作的数据 - - ### 级别1-应用或API 在最高级别,授权和访问策略实施可以控制实体是否有权访问应用程序或API。这种用例经常出现在公司环境中。如8-1所示,运营团队中的员工可能没有访问公财务系统的权限。这一级别的策略实施可以在应用程序内处理,也可以由应用程序前面的实体处理, @@ -37,21 +33,18 @@ 功能级授权和访问策略实施控制实体在应用程序或API中可以做什么。例如,财务部门的初级会计职员可能可以访问公司会计系统并输入个别日记账分录,但不能执行月末结账。这种级别的授权和访问策略实施往往是特定于应用程序的。它可以利用存储在身份和授权系统中的用户信息,例如身份系统中的角色或组,但通常在应用程序或API的逻辑中强制执行。 ### 第3级-数据访问 + 第三级授权和访问政策执行规定了对特定数据子集的访问。如果功能级访问策略强制定义了实体可以执行的功能,则数据级访问策略将进一步限制对特定数据的访问。例如,在运营系统的订单应用中,可以在功能级别授权角色或组为“区域销售经理”的用户查看销售订单,但数据级访问策略将其限制在用户身份属性中的“销售区域”属性中所示的特定区域。数据级访问可以在应用程序或API内或底层存储层中强制执行。 ## 用户实体和应用实体的授权 -接下来,我们将介绍两种需要授权和访问策略实施的场景。 -第一个场景是关于如何控制用户(或实体)在应用程序中做什么,第二个是如何控制应用程序可以请求什么API。 +接下来,我们将介绍两种需要授权和访问策略实施的场景。 第一个场景是关于如何控制用户(或实体)在应用程序中做什么,第二个是如何控制应用程序可以请求什么API。 用户需要授权才能在应用程序中执行各种功能,应用程序可以根据用户的授权权限呈现应用程序的用户界面,这样就不会显示用户不能使用的功能。此外,当用户发出请求时,应用程序后端或API必须在执行请求之前检查用户是否具有请求所需的授权。 应用程序需要授权才能调用受保护的API。如果API上的内容归应用程序的用户所有,则访问需要用户的授权。这种情况经常出现在面向消费者的应用程序中。如果应用程序以自己的名义访问API中的内容,则授权服务器将根据授权服务器管理员先前配置的权限向应用程序授予授权。 -无论被授权的实体是什么,控制访问通常涉及三个步骤: -•授权和访问策略的规范定义。 -•向执行点提供授权信息(如果需要) -•执行点执行访问策略 +无论被授权的实体是什么,控制访问通常涉及三个步骤: •授权和访问策略的规范定义。 •向执行点提供授权信息(如果需要) •执行点执行访问策略 ### 用户授权 @@ -75,20 +68,12 @@ ![9-2 example for Authz Delvery .jpg](https://i.loli.net/2021/07/20/W91QBsgYuM42dRS.jpg) -
9-2授权传递
- -1.用户重定向到在OIDC OpenID提供程序处登录。 -2.ID令牌包括用户购买的订阅信息。 -3.ID Token中的订阅数据用于确定显示的电影列表。 -4.用户选择要观看的电影。 -5.应用程序后端检查用户所选电影的订阅级别。 - +9-2授权传递 +1.用户重定向到在OIDC OpenID提供程序处登录。 2.ID令牌包括用户购买的订阅信息。 3.ID Token中的订阅数据用于确定显示的电影列表。 4.用户选择要观看的电影。 5.应用程序后端检查用户所选电影的订阅级别。 在该示例中,ID令牌中关于用户的订阅级别的信息用于向用户显示他们有权观看的电影。它还用于强制执行访问策略。尽管前端将电影列表限制在用户可以观看的范围内,但恶意用户可能会找到绕过此限制的方法,因此访问策略强制检查是在应用程序后端进行的,无法绕过此检查。本例使用OIDC,但是使用SAML2.0的应用程序可以遵循类似的模型,从SAML2.0断言中获取授权数据。 - - #### 策略执行 在信任令牌声明的信息之前,需要验证令牌的有效性,声明的是未经篡改的真实性,一般流程如下: @@ -101,8 +86,6 @@ 请务必查看OpenID提供程序的文档,以了解实现它们所需的确切验证步骤。一旦ID令牌被验证,应用程序就可以使用令牌中的声明来执行访问策略。 - - ### 应用授权 授权和策略实施的第二种情况是应用程序调用API。 @@ -111,23 +94,23 @@ 代表用户调用API的应用程序请求由用户授权,代表自己调用API的请求由授权服务器基于配置的策略授权。此策略通常是在应用程序调用API之前配置的。除非应用程序或API的数量很大,否则策略规范通常通过指示授权调用特定API的特定应用程序以及可以访问哪些端点和操作来表示。如果使用OAuth2.0,策略可以按作用域来指定,例如“get:documents” -##### 授权 +**授权** OAuth2.0授权将其请求的作用域指定为授权请求的参数。例如,应用程序请求OpenID提供者的UserInfo端点的访问令牌来检索用户属性,可能会使用“scope=OpenID profile email”。 -作用域扮演着非常重要的角色。如果没有声明,作用域只是一个字符串列表,其中包含作用域标记或作用域名称。示例:scope=“doc_read doc_write openid email”。 +作用域扮演着非常重要的角色。如果没有声明,作用域只是一个字符串列表,其中包含作用域标记或作用域名称。示例:scope=“doc\_read doc\_write openid email”。 -将作用域scope参数看作访问范围是种不错的方式,也就是说,它列出了客户需要访问的内容,可以用于粗粒度授权,以决定是否允许客户机查询API。但是,即使令牌中存在doc_read的作用域,我们也不知道应该允许它读取哪个文档(doc)。为了进一步说明,以OpenID Connect为例,它将作用域定义为一组声明的集合。因此,“email” Scope令牌定义为“email”和“email is verified”声明。声明具有与其关联的值,而作用域标记只是一个名称,这意味着作用域只是一组声明的打包,例如OIDC中的Profile,具有如下的结构: +将作用域scope参数看作访问范围是种不错的方式,也就是说,它列出了客户需要访问的内容,可以用于粗粒度授权,以决定是否允许客户机查询API。但是,即使令牌中存在doc\_read的作用域,我们也不知道应该允许它读取哪个文档(doc)。为了进一步说明,以OpenID Connect为例,它将作用域定义为一组声明的集合。因此,“email” Scope令牌定义为“email”和“email is verified”声明。声明具有与其关联的值,而作用域标记只是一个名称,这意味着作用域只是一组声明的打包,例如OIDC中的Profile,具有如下的结构: ![9-3 OIDC Profile Token.png](https://i.loli.net/2021/07/20/q7OI4NyCDo18seE.png) -
9-3 OIDC Profile
+9-3 OIDC Profile 事实上每个OpenID声明都与一个作用域相关联,如下图所示。 ![9-4 OIDC Scope.png](https://i.loli.net/2021/07/20/2irTDLGOSZXUMo4.png) -
9-3 OIDC Scopes
+9-3 OIDC Scopes 这种定义的方式可以推广到任意作用域和声明,当涉及API访问时,这将变得非常有用。 @@ -141,11 +124,7 @@ OAuth2.0授权将其请求的作用域指定为授权请求的参数。例如, API必须验证访问令牌并执行访问策略,以确保在响应之前允许应用程序的请求。验证和获取有关访问令牌的信息的步骤将因授权服务器对访问令牌的实现而异。您应该查看授权服务方文档,以了解有关如何验证它发出的任何令牌的详细信息。一旦验证了访问令牌,API就可以使用令牌中的信息(如果允许的话,包括任何自定义声明)在响应请求之前执行其访问策略强制。 - - ## 本章重点回顾 授权涉及特权的授予,而访问策略强制是在请求资源时进行的检查,以验证请求者是否已被授予请求所需的特权。授权和访问策略实施可用于控制用户在应用程序中可以做什么,以及应用程序是否可以向API发出请求。用户授权可基于用户配置文件中的静态因素和/或身份验证时评估的动态因素,两者都可以通过安全令牌传递给应用程序。应用程序授权可以基于用户或授权服务器批准的作用域,并在传递给应用程序并用于调用API的访问令牌中显示。在下一章中,我们将讨论一个示例应用程序,以及它如何使用OIDC和OAuth 2.0来授权对API的访问。 - - diff --git a/docs/modernidentity/ChapterOne.md b/docs/modernidentity/chapterone.md similarity index 94% rename from docs/modernidentity/ChapterOne.md rename to docs/modernidentity/chapterone.md index 4459fce..b6e67c0 100644 --- a/docs/modernidentity/ChapterOne.md +++ b/docs/modernidentity/chapterone.md @@ -4,8 +4,6 @@ 想象一个场景,你正在开启一个新的软件/在线应用项目,然后你花了大量的时间研究需求、推演方案,设计算法和功能。当你完成了这一系列工作,已经准备好把你关于项目的所有想法付诸实践时。突然你意识这个软件/应用程序需要有身份管理的功能,这时必不可少的你就需要开始考虑用户系统的设计。于是,您开始研究如何创建帐户。刚开始,你可能认为这就是个用户名和密码校验的工作,但是你慢慢的发现,为了更好的引入用户,可能需要社交登录;为了验证用户的安全性需要提供多因素身份验证;为了更好的营销需要引入更多的用户信息,你可能采用的微服务的架构,可能需要服务之间的认证;不同用户拥有不同的权限这时需要基于身份的授权;为了更好的用户体验,需要以上所有这些用户的设计能够跨多个设备使用,等等。这时你才会发现自己在和一个多头的妖怪在战斗,当你解决一个头以后,不断又有新的头冒出来,让你疲于和它战斗,而让你的项目和想法迟滞。如果没有一个很好的计划来处理身份管理,那么解决一个身份管理挑战可能会导致更多的挑战。 - - ## 身份挑战 虽然身份管理在理论上是一个简单的概念,但要使其在实践中如何让身份成为一个支撑而不是一个负担,需要多种因素共同作用。提供良好的用户体验是基础的要求,还需要平衡来自业务需求和安全性的无数期望。因此,身份需要仔细的规划、设计和开发来实现对正在进行的项目提供的良好支撑。不幸的是,身份管理没有一个一刀切的方案,我们没有办法能够构建的一个完美的解决方案适合每一个场景和用例。 @@ -34,8 +32,6 @@ 构建应用程序时的另一个主要关注点是管理敏感身份数据处理和隐私保护的法规。欧盟的GDPR(General Data Privacy Regulation)、中国的数据安全法等法规以及其他地方正在颁布的类似法规的出台,对于收集或处理用户数据的应用程序要求越来越严格,必须符合隐私要求,因为如果违反这些要求,可能会招致严厉的处罚。对于应用程序设计身份管理需要考虑的环节也越来越多。 - - ## 目标 我们写这本书的目的是介绍如何做好身份管理,这些主要根据我们构建和部署应用程序的经验,以及结合目前主流的市场上的解决方案而来。本书重点是软件应用程序的身份管理方面,如创建帐户、身份验证、API授权、单点登录、帐户管理和注销用户。 @@ -58,58 +54,44 @@ 在此之后的章节将介绍用户第一次登录后发生的事情,包括有关会话、单点登录、增强的身份验证形式、帐户管理、注销和取消设置等。如果您的应用程序第一次不能完美地工作,我们包含了一章关于故障排除的指导。我们还分享了可能出现的问题场景的信息,以及我们遇到的一些不寻常的用例。最后,我们简要介绍了法规遵从性以及导致一些非常不幸的违规行为的一些错误。 - - > 术语 我们讨论的协议都使用了不同的术语。不同的协议对于术语有不同的定义,这使得很难对某些组件使用一致的术语。最终我们采用的方式是,在特定协议的章节中,我们使用该协议使用的特定术语。而在其他章节中,我们将使用更通用的术语。例如,在OAuth2.0这一章中,我们提到了授权服务器;在OIDC这一章中,OpenID提供者;在SAML 2.0章节,身份提供者。在其他更一般的章节中,我们使用术语IdP 身份提供者来对用户进行身份验证。 -| 协议 | 术语 | 中文 | -| -------- | -------------------- | ------------------- | -| OAuth2.0 | Authorization Server | 授权服务器-IdP | -| OIDC | Openid Provider | OpenID提供者-IdP | -| SAML | IdP | 身份提供者-IdP | -| OAuth2.0 | RO(Resource Owner) | 资源所有者-用户 | -| OIDC | EU (End User) | 终端用户-用户 | -| SAML | User | 用户-用户 | -| OAuth2.0 | Client | 客户端-应用程序 | -| OIDC | Relying Party | 依赖方-应用程序 | -| SAML | Service Provider | 服务提供方-应用程序 | +| 协议 | 术语 | 中文 | +| :--- | :--- | :--- | +| OAuth2.0 | Authorization Server | 授权服务器-IdP | +| OIDC | Openid Provider | OpenID提供者-IdP | +| SAML | IdP | 身份提供者-IdP | +| OAuth2.0 | RO(Resource Owner) | 资源所有者-用户 | +| OIDC | EU (End User) | 终端用户-用户 | +| SAML | User | 用户-用户 | +| OAuth2.0 | Client | 客户端-应用程序 | +| OIDC | Relying Party | 依赖方-应用程序 | +| SAML | Service Provider | 服务提供方-应用程序 | 在我们的术语中,客户端应用程序是一个例外。跨这些协议的客户端有许多名称 – 客户端、依赖方、服务提供商、客户应用程序。客户和依赖方在某些规范中的含义是不同的。为了方便理解,我们选择在整个过程中使用术语“application”,即应用程序,以指通过OAuth 2.0、OIDC或SAML 2.0发出身份验证或API授权请求的应用程序。我们也将终端用户简单的称为用户,因为我们不需要区分不同类型的用户。 ## 示例程序 -为了更好的说明,我们将提供了一个使用OIDC和OAuth2.0协议的示例应用程序。通过示例,介绍身份协议是如何使用的。 +为了更好的说明,我们将提供了一个使用OIDC和OAuth2.0协议的示例应用程序。通过示例,介绍身份协议是如何使用的。 在开始阅读本文或者开始设计身份系统之前,我们建议考虑以下问题,带着这些问题或许对于后续章节要解决的问题有更深入的理解: **问题设计** * 身份的需要包含哪些类型用户:员工、客户、合作伙伴; - * 他们的身份标识是一个或者是多个,如何关联和映射; - * 不同身份标识对应的认证方式和可信源是什么; - * 通过何种应用进行接入,是web、api、还是native app; - * 如果进行服务的访问控制,是否需要根据以上给予不同的授权; - * 是否需要增强的身份,给予的认证有效期; - * 多个应用之间的认证是否共享,即SSO; - * 多个应用之间的身份是否关联等等 - * 否涉及到敏感信息的操作,如有是否符合了法规; - * 当用户登出式,会发生哪些事件; - * 需要满足哪些合规性的需求。 - - ## 本章关键点回顾 现代用户希望在使用应用程序时获得无摩擦、设计良好的体验。身份管理应该帮助他们快速访问应用程序,而不是妨碍他们。为了实现这一点,开发人员面临许多问题,需要在开发身份管理时对各种可用选项进行规划。下一章将帮助您了解身份管理的组件, 通过覆盖身份生命周期的事件来解决问题。 diff --git a/docs/modernidentity/ChapterSeven.md b/docs/modernidentity/chapterseven.md similarity index 62% rename from docs/modernidentity/ChapterSeven.md rename to docs/modernidentity/chapterseven.md index fc8840d..3856b5e 100644 --- a/docs/modernidentity/ChapterSeven.md +++ b/docs/modernidentity/chapterseven.md @@ -1,7 +1,6 @@ # 第七章 OpenID Connect -如前一章所述,OAuth2.0提供了一个框架,用于授权应用程序调用API,但不是为验证应用程序的用户而设计的。 -因为互联网的应用越来越多,不同的应用之间需要共享身份,亟需一个大家公认的标准来进行身份的共享。同时,因为技术的演进(微服务、云原生),对于API的授权需求越来越多。OAuth2.0的一些实现为此添加了专有的附加内容,但是需要一个标准的解决方案。OpenID Connect(OIDC)[i](https://openid.net/connect/) 协议在Oauth2.0之上提供了一个身份服务层,旨在允许授权服务器对应用程序的用户进行身份验证,并以标准的方式返回结果。在本章中,我们将更详细地描述OIDC解决的问题和应用程序如何使用OIDC对用户进行身份验证。 +如前一章所述,OAuth2.0提供了一个框架,用于授权应用程序调用API,但不是为验证应用程序的用户而设计的。 因为互联网的应用越来越多,不同的应用之间需要共享身份,亟需一个大家公认的标准来进行身份的共享。同时,因为技术的演进(微服务、云原生),对于API的授权需求越来越多。OAuth2.0的一些实现为此添加了专有的附加内容,但是需要一个标准的解决方案。OpenID Connect(OIDC)[i](https://openid.net/connect/) 协议在Oauth2.0之上提供了一个身份服务层,旨在允许授权服务器对应用程序的用户进行身份验证,并以标准的方式返回结果。在本章中,我们将更详细地描述OIDC解决的问题和应用程序如何使用OIDC对用户进行身份验证。 ## 解决的问题 @@ -11,16 +10,15 @@ OIDC协议旨在解决某些场景下,用户需要通过身份验证才能访 ![7-1 OIDC.jpg](https://i.loli.net/2021/07/16/Z9zF7KAjG8VlOPE.jpg) -
7-1 OIDC 概览
+7-1 OIDC 概览 -当用户访问应用程序时,它会将用户的浏览器重定向到实现OIDC的授权服务器。 OIDC将这样的授权服务器称为OpenID提供者(OpenID Provider),我们将在本章中使用该术语。OpenID提供者与用户交互以验证他们的身份(假设他们还没有登录)。身份验证后,用户的浏览器将重定向回应用程序。应用程序可以请求在称为ID令牌的安全令牌中返回关于已验证用户的声明。或者,它可以请求Oauth2.0访问令牌,并使用它调用OpenID提供者的UserInfo端点来获取声明。因为OIDC是OAuth2.0之上的一层 ,应用程序可以使用OpenID提供程序进行用户身份验证和授权,以调用OpenID提供程序的API。通过这个示例,我们了解了OIDC的基本概念。下一节将定义一些附加术语,以便我们提供进一步的细节和更准确的描述。 +当用户访问应用程序时,它会将用户的浏览器重定向到实现OIDC的授权服务器。 OIDC将这样的授权服务器称为OpenID提供者\(OpenID Provider\),我们将在本章中使用该术语。OpenID提供者与用户交互以验证他们的身份(假设他们还没有登录)。身份验证后,用户的浏览器将重定向回应用程序。应用程序可以请求在称为ID令牌的安全令牌中返回关于已验证用户的声明。或者,它可以请求Oauth2.0访问令牌,并使用它调用OpenID提供者的UserInfo端点来获取声明。因为OIDC是OAuth2.0之上的一层 ,应用程序可以使用OpenID提供程序进行用户身份验证和授权,以调用OpenID提供程序的API。通过这个示例,我们了解了OIDC的基本概念。下一节将定义一些附加术语,以便我们提供进一步的细节和更准确的描述。 ## 术语 我们将对以上提到的术语进行进一步准确的定义和解释: -* 最终用户End User – 最终用户是要验证的主体。 (我们将使用术语“用户”来简化和保持各章节的一致性。) - +* 最终用户End User – 最终用户是要验证的主体。 \(我们将使用术语“用户”来简化和保持各章节的一致性。) * OpenID提供者(OP) – OpenID提供程者是Oauth2.0授权服务器实现OIDC,并对用户进行身份验证,将有关已验证用户和身份验证事件的声明返回给依赖方(应用程序)。 * 依赖方(RP) – OAuth 2.0 将用户身份验证委托给OpenID提供者,并从OpenID提供程序请求有关用户的声明的客户端。对于依赖方,我们通常使用“应用”一词 来保持跨章节的一致性。 @@ -46,17 +44,13 @@ OIDC除了利用OAuth 2.0中描述的的授权和令牌端点以外,增加加U UserInfo Endpoint–返回关于经过身份验证的用户的声明。调用端点需要访问令牌,并且返回的声明由访问令牌控制。 - - ### ID Token ID Token令牌是OpenID提供者用来向应用程序传递关于身份验证事件和身份验证用户的声明的安全令牌。ID令牌以Json Web Token(JWT)[iii](https://tools.ietf.org/html/rfc7519)格式编码。图7-2显示了一个示例ID令牌。 - - ![7-2 OIDC IDToken.jpg](https://i.loli.net/2021/07/16/IraHYnfyRzp8qTl.jpg) -
7-2 ID Token
+7-2 ID Token JWT格式旨在在双方之间传递声明。作为JWT,ID令牌由报头(Header)、有效负载(Payload)和签名(Signature)组成。最终,这些组合在一起通过base-64进行编码。 @@ -69,11 +63,8 @@ JWT格式旨在在双方之间传递声明。作为JWT,ID令牌由报头(Hea 包含关于用户和身份验证事件的声明。主要包括: * 发行人 - * 主体或经过身份验证的用户(通常是资源所有者) - * 用户如何认证以及何时认证 - * 令牌的对象(即受众) 这些标记非常灵活,允许您添加自己的声明 @@ -86,24 +77,22 @@ JWT格式旨在在双方之间传递声明。作为JWT,ID令牌由报头(Hea OIDC规范中,ID令牌JWT的Payload定义了一组适用于所有类型的OIDC身份验证的用户和身份验证事件的声明[vi](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)。如表7-1所示。 -| Claims 声明 | 含义 | -| ----------- | ------------------------------------------------------------ | -| iss | ID令牌的颁发者,以URL格式标识。颁发者通常是OpenID提供者。“iss”声明不应包括URL查询或片段组件。 | -| sub | 唯一的(在OpenID提供者中),对已验证用户或主题实体区分大小写的字符串标识符,长度不超过255个ASCII字符。决不应将子声明中的标识符重新分配给新用户或实体。 | -| aud | ID令牌所针对的依赖方(应用程序)的客户端ID。单个区分大小写的字符串,如果有多个访问群体,则可以是区分大小写的字符串的数组。 | -| jti | Jti声明为JWT提供了唯一的标识符。 实现JTI以唯一地标识JWT可以帮助防止攻击者发送相同JWT以发出请求的重放攻击.服务器将生成jti值,并在每个响应上将其与新的JWT一起发送.当收到新请求时,服务器必须验证jti值(以确保它之前没有使用过). | -| iat | 颁发ID令牌的时间,指定为自1970年1月1日00:00:00 UTC到ID令牌颁发时间的秒数。 | -| exp | ID令牌的过期时间,指定为自1970年1月1日00:00:00 UTC到令牌过期时间的秒数。应用程序必须在这个时间之后考虑ID令牌无效,允许对时钟偏移允许几分钟的公差。 | -| idp | 颁发令牌的IDP的唯一标识 | -| auth_time | 用户通过身份验证的时间,指定为自1970年1月1日00:00:00 UTC到身份验证时间的秒数。 | -| nonce | 从依赖方传入的身份验证请求中传递的不可使用的、区分大小写的字符串值,由OpenID提供程序添加到ID令牌中,以将ID令牌链接到依赖方应用程序会话,并便于检测重播的ID令牌。 | -| amr | 【可选】身份验证方法引用的字符串–用于指示用于对ID令牌的主题实体进行身份验证的身份验证方法。认证方法参考值规范[vii](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-amr-values-04)为该声明定义了一组初始标准值。 | -| acr | 【可选】包含身份验证上下文类引用的字符串–用于指示用于对ID令牌的主题进行身份验证的身份验证机制的身份验证上下文类。值可以由OpenID提供者决定,也可以由依赖方和OpenID提供者商定,并且可以使用诸如OpenID Connect扩展身份验证配置文件ACR值草案之类的标准[viii](https://openid.net/specs/openid-connect-eap-acr-values-1_0.html) | -| azp | 【可选】Authorized party,结合aud使用。只有在被认证的一方和受众(aud)不一致时才使用此值,一般情况下很少使用。 | - -ID令牌可以包含表7-1中所列之外的额外声明。可以添加的附加声明包含:username, given_name, family_name, email, email_verified, locale, and picture等等。附加标准声明的列表可以参考OIDC核心规范的第5.1节[ix](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)。特定类型的OIDC请求(流)可能涉及附加声明。也可以添加自定义的声明,如IDP等。 - - +| Claims 声明 | 含义 | +| :--- | :--- | +| iss | ID令牌的颁发者,以URL格式标识。颁发者通常是OpenID提供者。“iss”声明不应包括URL查询或片段组件。 | +| sub | 唯一的(在OpenID提供者中),对已验证用户或主题实体区分大小写的字符串标识符,长度不超过255个ASCII字符。决不应将子声明中的标识符重新分配给新用户或实体。 | +| aud | ID令牌所针对的依赖方(应用程序)的客户端ID。单个区分大小写的字符串,如果有多个访问群体,则可以是区分大小写的字符串的数组。 | +| jti | Jti声明为JWT提供了唯一的标识符。 实现JTI以唯一地标识JWT可以帮助防止攻击者发送相同JWT以发出请求的重放攻击.服务器将生成jti值,并在每个响应上将其与新的JWT一起发送.当收到新请求时,服务器必须验证jti值\(以确保它之前没有使用过\). | +| iat | 颁发ID令牌的时间,指定为自1970年1月1日00:00:00 UTC到ID令牌颁发时间的秒数。 | +| exp | ID令牌的过期时间,指定为自1970年1月1日00:00:00 UTC到令牌过期时间的秒数。应用程序必须在这个时间之后考虑ID令牌无效,允许对时钟偏移允许几分钟的公差。 | +| idp | 颁发令牌的IDP的唯一标识 | +| auth\_time | 用户通过身份验证的时间,指定为自1970年1月1日00:00:00 UTC到身份验证时间的秒数。 | +| nonce | 从依赖方传入的身份验证请求中传递的不可使用的、区分大小写的字符串值,由OpenID提供程序添加到ID令牌中,以将ID令牌链接到依赖方应用程序会话,并便于检测重播的ID令牌。 | +| amr | 【可选】身份验证方法引用的字符串–用于指示用于对ID令牌的主题实体进行身份验证的身份验证方法。认证方法参考值规范[vii](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-amr-values-04)为该声明定义了一组初始标准值。 | +| acr | 【可选】包含身份验证上下文类引用的字符串–用于指示用于对ID令牌的主题进行身份验证的身份验证机制的身份验证上下文类。值可以由OpenID提供者决定,也可以由依赖方和OpenID提供者商定,并且可以使用诸如OpenID Connect扩展身份验证配置文件ACR值草案之类的标准[viii](https://openid.net/specs/openid-connect-eap-acr-values-1_0.html) | +| azp | 【可选】Authorized party,结合aud使用。只有在被认证的一方和受众(aud)不一致时才使用此值,一般情况下很少使用。 | + +ID令牌可以包含表7-1中所列之外的额外声明。可以添加的附加声明包含:username, given\_name, family\_name, email, email\_verified, locale, and picture等等。附加标准声明的列表可以参考OIDC核心规范的第5.1节[ix](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)。特定类型的OIDC请求(流)可能涉及附加声明。也可以添加自定义的声明,如IDP等。 ## OIDC如何工作 @@ -121,9 +110,7 @@ OIDC授权代码流类似于OAuth2.0授权代码授,整个流程中,主要 ![7-3 OIDC CodeFlow.jpg](https://i.loli.net/2021/07/16/jSY3WfZekLt4GO7.jpg) -
OIDC 授权码流程
- - +OIDC 授权码流程 1. 用户向应用(RP)发起请求 2. 应用向OpenID 提供者发出一个请求,发送一个基本的授权范围,该请求通过浏览器进行重定向。 @@ -142,7 +129,7 @@ OIDC授权代码流类似于OAuth2.0授权代码授,整个流程中,主要 应用程序通过浏览器将用户的身份验证请求重定向到OpenID提供程序的授权端点,例如 -``` +```text GET /authorize? response_type=code @@ -162,21 +149,21 @@ response_type=code 该示例中使用的参数如表7-2所示,但不同的OpenID提供者会有所差异。 -| 参数 | 说明 | -| --------------------- | ------------------------------------------------------------ | -| response_type | 指示OIDC授权类型。“code”用于指示该流程为授权码流程。 | -| client_id | 应用程序的标识符,在向OpenID服务器注册时分配。 | -| response_mode | 一个可选参数,请求授权服务器用于向客户端应用程序传递响应参数为非默认方式时,可以指定该参数 | -| state | 一个不可猜测的字符串,对于每个调用都是唯一的,对授权服务器来说是不透明的,客户端使用它来跟踪相应请求和响应之间的状态,以降低CSRF攻击的风险。它应该包含一个将请求与用户会话相关联的值。这可以通过将会话cookie或其他会话标识符的散列与附加的每个请求唯一组件连接起来来实现。当收到响应时,客户机应确保响应中的状态参数与从同一浏览器发送的请求的状态参数匹配。 | -| scope | 本次请求授予权限的范围。例如:openid,profile,email | -| nonce | 在请求中传递给OpenID提供者的不可猜测的值,如果请求了ID令牌,则在ID令牌中返回该值未修改的声明。用于防止令牌重放。 | -| redirect_uri | 指示应用程序的回调URL,OpenID提供者将其带有授权代码的响应发送到此URL。 | -| code_challenge | PKCE挑战码,由PKCE编码验证码派生 | -| code_challenge_method | PKCE中指示生成挑战码的派生方法。 “S256” or “plain.” | +| 参数 | 说明 | +| :--- | :--- | +| response\_type | 指示OIDC授权类型。“code”用于指示该流程为授权码流程。 | +| client\_id | 应用程序的标识符,在向OpenID服务器注册时分配。 | +| response\_mode | 一个可选参数,请求授权服务器用于向客户端应用程序传递响应参数为非默认方式时,可以指定该参数 | +| state | 一个不可猜测的字符串,对于每个调用都是唯一的,对授权服务器来说是不透明的,客户端使用它来跟踪相应请求和响应之间的状态,以降低CSRF攻击的风险。它应该包含一个将请求与用户会话相关联的值。这可以通过将会话cookie或其他会话标识符的散列与附加的每个请求唯一组件连接起来来实现。当收到响应时,客户机应确保响应中的状态参数与从同一浏览器发送的请求的状态参数匹配。 | +| scope | 本次请求授予权限的范围。例如:openid,profile,email | +| nonce | 在请求中传递给OpenID提供者的不可猜测的值,如果请求了ID令牌,则在ID令牌中返回该值未修改的声明。用于防止令牌重放。 | +| redirect\_uri | 指示应用程序的回调URL,OpenID提供者将其带有授权代码的响应发送到此URL。 | +| code\_challenge | PKCE挑战码,由PKCE编码验证码派生 | +| code\_challenge\_method | PKCE中指示生成挑战码的派生方法。 “S256” or “plain.” | -
7-2 OIDC 认证请求参数
+7-2 OIDC 认证请求参数 -身份验证请求中的response_type参数用于指示所需的OIDC流。可选参数response_mode控制将响应参数返回到应用程序的方法。除非另有说明,否则一般不指定response_mode,默认响应模式是使用查询参数或散列片段方式在重定向uri中将授权码、安全令牌等返回。 +身份验证请求中的response\_type参数用于指示所需的OIDC流。可选参数response\_mode控制将响应参数返回到应用程序的方法。除非另有说明,否则一般不指定response\_mode,默认响应模式是使用查询参数或散列片段方式在重定向uri中将授权码、安全令牌等返回。 OAuth2.0中的Scope参数用于请求通过访问令牌授予API特权。对于OIDC身份验证请求,作用域用于指示OIDC的使用,并请求有关已验证用户信息的特定声明。OIDC身份验证请求必须包含“openid”,“profile”用于包含请求默认的用户配置文件声明的授权,例如name、family name和given name。“email”用于请求用户的电子邮件地址以及该地址是否已被验证。如果是通过访问令牌去请求向OpenID提供者的UserInfo端点,这些作用域将会限制应用请求返回用户信息时的权限,只会返回被授予的作用域范围内的声明。如果未颁发访问令牌,则请求的声明将包含在ID令牌中。有关请求声明的更多详细信息,请参见OpenID Connect Core规范的第5.4节和第5.5节[x](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims)。 @@ -188,7 +175,7 @@ OAuth2.0中的Scope参数用于请求通过访问令牌授予API特权。对于O OpenID提供程序返回对身份验证请求中指定的重定向URI的响应,该URI必须在OpenID提供者处注册。对于授权码流,默认响应模式使用查询参数将授权码返回到身份验证请求中指定的重定向URI(回调),同时返回在身份验证请求中传递的状态值。 -``` +```text HTTP/1.1 302 Found Location: https://clientapplication.com/callback? @@ -204,7 +191,7 @@ code= 应用程序使用授权码向OpenID提供者的令牌端点请求令牌。下面的示例为非公共客户端使用其密钥,通过HTTP基本身份验证,向OpenID提供者的令牌端点请求令牌。 -``` +```text POST /token HTTP/1.1 Host: authorizationserver.com @@ -224,20 +211,18 @@ grant_type=authorization_code 应用程序使用授权码向OpenID提供者的令牌端点请求令牌时,需要进行客户端的认证,OIDC核心规范第9节[xii](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)定义了身份验证方法的更多信息,具体方式可以参阅规范。令牌请求示例的参数如表7-3所示。 +| 参数 | 说明 | +| :--- | :--- | +| grant\_type | 指示授权类型,指定为“code”用于指示该流程为授权码流程。 | +| code | 授权响应中收到的授权码 | +| code\_verifier | 为了派生代码挑战码而产生的PKCE的验证码值。它应该是一个长度介于43到128个字符(含)之间的不可使用的加密随机字符串,使用字符A–Z、A–Z、0–9、“-”、“.”、“”和“~” | +| redirect\_uri | 授权服务器响应的回调URI。 应该与授权请求中传递给授权端点的重定向uri的值相匹配 | - -| 参数 | 说明 | -| ------------- | ------------------------------------------------------------ | -| grant_type | 指示授权类型,指定为“code”用于指示该流程为授权码流程。 | -| code | 授权响应中收到的授权码 | -| code_verifier | 为了派生代码挑战码而产生的PKCE的验证码值。它应该是一个长度介于43到128个字符(含)之间的不可使用的加密随机字符串,使用字符A–Z、A–Z、0–9、“-”、“.”、“”和“~” | -| redirect_uri | 授权服务器响应的回调URI。 应该与授权请求中传递给授权端点的重定向uri的值相匹配 | - -
7-3 令牌请求参数
+7-3 令牌请求参数 OpenID提供者将以JSON格式响应请求的令牌。下面是一个示例响应: -``` +```text HTTP/ 1.1 200 OK Content-Type: application/json;charset=UTF-8 @@ -263,42 +248,36 @@ Pragma: no-cache 响应包含的参数如表7-4所示 -| 参数 | 说明 | -| ------------- | ------------------------------------------------------------ | -| id_token | 包含用户信息声明的身份令牌 | -| access_token | 用于调用User Endpoint API的访问令牌。 | -| token_type | 颁发的令牌类型。 | -| expires_in | 令牌的有效期 | -| refresh_token | 刷新令牌 是可选的。 是否返回刷新令牌由授权服务器自行决定。有关更多信息,请参阅本章后面的刷新令牌部分。 | +| 参数 | 说明 | +| :--- | :--- | +| id\_token | 包含用户信息声明的身份令牌 | +| access\_token | 用于调用User Endpoint API的访问令牌。 | +| token\_type | 颁发的令牌类型。 | +| expires\_in | 令牌的有效期 | +| refresh\_token | 刷新令牌 是可选的。 是否返回刷新令牌由授权服务器自行决定。有关更多信息,请参阅本章后面的刷新令牌部分。 | -
7-4 令牌响应参数
+7-4 令牌响应参数 在信任ID令牌中的声明之前,应用程序应按照发布OpenID提供者提供的指导,JWT规范[xiii](https://datatracker.ietf.org/doc/html/rfc7519#section-7.2)中的验证步骤验证ID令牌。应用程序可以从ID令牌或通过使用访问令牌调用OpenID提供者的UserInfo端点来获取关于已验证用户的声明。 - - ### OIDC隐式模式 -OIDC中的隐式流程与OAuth2.0的隐式授权类型相似。 -如第6章所述,不建议使用OAuth2.0隐式授权来获取访问令牌,至少在默认响应模式下是这样。然而,该指导原则的前提是基于暴露URL片段中的访问令牌的风险,即该URL片段可能通过浏览器历史记录或referer头泄漏。如果,应用程序只需要对用户进行身份验证并且可以通过ID令牌获取用户信息,而不需要访问令牌。在这种情况下,隐式流是可以接受的。图7-4显示了应用程序仅接收ID令牌的流程。 +OIDC中的隐式流程与OAuth2.0的隐式授权类型相似。 如第6章所述,不建议使用OAuth2.0隐式授权来获取访问令牌,至少在默认响应模式下是这样。然而,该指导原则的前提是基于暴露URL片段中的访问令牌的风险,即该URL片段可能通过浏览器历史记录或referer头泄漏。如果,应用程序只需要对用户进行身份验证并且可以通过ID令牌获取用户信息,而不需要访问令牌。在这种情况下,隐式流是可以接受的。图7-4显示了应用程序仅接收ID令牌的流程。 ![7-4 OIDC implicit.jpg](https://i.loli.net/2021/07/16/ZDIVfbOosuFrktc.jpg) -
7-4 OIDC 隐式流程
+7-4 OIDC 隐式流程 1. 用户向应用(RP)发起请求 - 2. 应用向OpenID提供者发出一个请求,发送一个基本的授权范围,该请求通过浏览器进行重定向。 - 3. OpenID提供者器接下来将向用户发起身份验证,和征求用户的授权同意范围。 - 4. 用户通过输入凭据进行身份验证,并且对请求的权限进行授权,该授权将影响令牌中包含影响作用域的信息。 5. OpenID提供者生成本次授权请求的身份令牌(IDToken),然后将此令牌包含在重定向请求中,通过浏览器将令牌返回给应用。 6. 应用程序从ID令牌获取关于用户身份的声明,并向用户显示需要展示的内容。 使用隐式流程对用户进行身份验证的(仅请求ID令牌)身份验证请求,如下: -``` +```text GET /authorize? response_type=id_token @@ -313,29 +292,27 @@ response_type=id_token & redirect_uri= HTTP/1.1 -Host: authorizationserver.com - - +Host: authorizationserver.com ``` 隐式流身份验证请求中使用的参数,除了响应类型值意外,其他参数与表7-2所示相同的定义。对于隐式流,允许的响应类型值为: -* id_token--响应仅包含idtoken -* id_token token--响应包含id token和access token +* id\_token--响应仅包含idtoken +* id\_token token--响应包含id token和access token 默认情况下,隐式流使用URL片段通过前端浏览器交互将所有令牌返回到重定向URI。 > 注意 -不建议在默认响应模式下使用“id_token token”的response_type,因为它会通过referer头或浏览器的历史记录使访问令牌置于暴露于潜在的风险。如果ID令牌不包含敏感数据,则使用默认响应模式和“id_token”则无此风险。 +不建议在默认响应模式下使用“id\_token token”的response\_type,因为它会通过referer头或浏览器的历史记录使访问令牌置于暴露于潜在的风险。如果ID令牌不包含敏感数据,则使用默认响应模式和“id\_token”则无此风险。 -对于只需要ID令牌的应用程序,应考虑使用非默认de form_post响应模式,因为响应参数编码在post响应的主体中,这避免了通过一个URL片段公开ID令牌和数据,但是这种响应模式对于某些应用可能是不可行的。需要敏感的访问令牌和或ID令牌的公共客户端应该考虑使用PKCE授权码流来代替。 +对于只需要ID令牌的应用程序,应考虑使用非默认de form\_post响应模式,因为响应参数编码在post响应的主体中,这避免了通过一个URL片段公开ID令牌和数据,但是这种响应模式对于某些应用可能是不可行的。需要敏感的访问令牌和或ID令牌的公共客户端应该考虑使用PKCE授权码流来代替。 #### 响应 -下面显示对隐式流程身份验证请求的示例响应,该请求使用“id_token”响应类型,仅请求id令牌。 +下面显示对隐式流程身份验证请求的示例响应,该请求使用“id\_token”响应类型,仅请求id令牌。 -``` +```text HTTP/1.1 302 Found Location: https://clientapplication.com/callback# @@ -345,53 +322,42 @@ id_token= & state = ``` - - ### OIDC 混合流程 OIDC混合流包含授权代码流和隐式流程的元素。它是为既有安全后端,又有在前端执行JavaScript的客户端的应用程序而设计的。混合流支持诸如在对应用程序前端的前通道响应中返回ID令牌和授权码、让应用程序后端使用授权码从令牌端点获取访问令牌(和可选的刷新令牌)之类的模型。该流程如图7-5所示。 ![7-5-HybridFlow.jpg](https://i.loli.net/2021/07/16/IqRwzpSukUD3hsT.jpg) -
7-5 OIDC 混合流程
- - +7-5 OIDC 混合流程 1. 用户向应用(RP)发起请求 - 2. 应用向OpenID提供者发出一个请求,发送一个基本的授权范围,该请求通过浏览器进行重定向。 - 3. OpenID提供者接下来将向用户发起身份验证,和征求用户的授权同意。 - 4. 用户通过输入凭据进行身份验证,并且对请求的权限进行授权,该授权将影响令牌中包含影响作用域的信息。 5. OpenID提供者生成本次授权请求的授权码和IDToken,然后将此授权码和IDToken包含在重定向请求中,通过浏览器将授权码和ID Token返回给应用。 6. 客户端验证ID Token,如果合法,应用后端使用授权码向OpenID提供者的令牌端点发送获取令牌请求,以获取访问令牌。 7. 授权服务器向该应用颁发授权令牌(AccessToken),刷新令牌(可选)。 8. 应用使用此令牌来访问用户信息。 - - 身份验证请求的参数如表7-2所示,但混合流的响应类型使用三个不同的值来控制从OpenID提供者的授权端点返回响应中的令牌。可以通过对令牌端点的后续令牌请求来请求其他令牌。表7-5总结了响应类型的可能值。 -| response_type | 响应包含的类型 | -| ------------------- | ------------------------------------------------------------ | -| code id_token | 返回code,id_token | -| code token | 返回code,访问令牌 access token——不建议使用默认的response_mode | -| code id_token token | 返回code,身份令牌 ID Token,访问令牌 access token——不建议使用默认的response_mode | +| response\_type | 响应包含的类型 | +| :--- | :--- | +| code id\_token | 返回code,id\_token | +| code token | 返回code,访问令牌 access token——不建议使用默认的response\_mode | +| code id\_token token | 返回code,身份令牌 ID Token,访问令牌 access token——不建议使用默认的response\_mode | > 注意 不建议在默认响应模式下使用通过授权端点的前端通道响应返回访问令牌的响应类型,即“code token”和“code id token token”,因为访问令牌将在浏览器中作为URL片段公开,并且可能通过referer标头或浏览器历史中泄漏。 -如果使用默认响应模式,则应使用“code id_token”响应类型,仅使用前通道响应将id令牌和授权码返回到浏览器。然后,通过应用程序后端的安全通道交互,可以从OpenID提供者的令牌端点获得访问令牌和可选刷新令牌。 +如果使用默认响应模式,则应使用“code id\_token”响应类型,仅使用前通道响应将id令牌和授权码返回到浏览器。然后,通过应用程序后端的安全通道交互,可以从OpenID提供者的令牌端点获得访问令牌和可选刷新令牌。 在实际应用中,混合流的应用并不广泛。使用此流需要一个实现,该实现将同时提供前端和后端信息,例如nonce和state,用这些信息验证它们接收到的任何响应和安全令牌,并防止诸如CSRF或令牌注入之类的攻击。应用程序应该考虑使用具有PKCE的授权代码流,除非它们具有需要混合流的特定用例。 +下面的示例中显示了使用混合流和“code id\_token”响应类型和默认响应模式的示例身份验证请求,参数定义与前面的示例类似。 - -下面的示例中显示了使用混合流和“code id_token”响应类型和默认响应模式的示例身份验证请求,参数定义与前面的示例类似。 - -``` +```text GET /authorize? response_type=code%20id_token @@ -406,8 +372,7 @@ response_type=code%20id_token &nonce= HTTP/1.1 -Host: authorizationserver.com - +Host: authorizationserver.com ``` 当应用程序接收到响应时,应用程序后端可以使用授权码去请求更多的令牌,如前一节中对授权码流所述。 @@ -418,19 +383,17 @@ Host: authorizationserver.com 对UserInfo端点的示例应用程序请求如下所示: -``` +```text GET /userinfo HTTP/1.1 Host: authorizationserver.com Authorization: Bearer - - ``` OpenID提供者的UserInfo端点响应返回带有JSON对象的声明(除非使用签名或加密的响应)。下面的示例是请求范围为“openid profile email”响应。 -``` +```text HTTP/1.1 200 OK Content-Type: application/json @@ -454,12 +417,8 @@ Content-Type: application/json "picture":"https://oneauth.com/profile/yang.yu/yang.yu.jpg", } - - ``` - - 如果所需的用户配置文件声明对于通过URL片段返回的ID令牌来说太大,则使用UserInfo端点来返回用户的信息。 ## 本章重点回顾 @@ -473,29 +432,29 @@ OIDC允许应用程序将用户身份验证委托给OpenID提供者,同时使 ## 参考: -i: https://openid.net/connect/ +i: [https://openid.net/connect/](https://openid.net/connect/) -ii:https://datatracker.ietf.org/doc/html/rfc8252 +ii:[https://datatracker.ietf.org/doc/html/rfc8252](https://datatracker.ietf.org/doc/html/rfc8252) -iii:https://tools.ietf.org/html/rfc7519 +iii:[https://tools.ietf.org/html/rfc7519](https://tools.ietf.org/html/rfc7519) -iv:https://datatracker.ietf.org/doc/html/rfc7515 +iv:[https://datatracker.ietf.org/doc/html/rfc7515](https://datatracker.ietf.org/doc/html/rfc7515) -v:https://datatracker.ietf.org/doc/html/rfc7516 +v:[https://datatracker.ietf.org/doc/html/rfc7516](https://datatracker.ietf.org/doc/html/rfc7516) -vi:https://openid.net/specs/openid-connect-core-1_0.html#IDToken +vi:[https://openid.net/specs/openid-connect-core-1\_0.html\#IDToken](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) -vii:https://datatracker.ietf.org/doc/html/draft-ietf-oauth-amr-values-04 +vii:[https://datatracker.ietf.org/doc/html/draft-ietf-oauth-amr-values-04](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-amr-values-04) -viii:https://openid.net/specs/openid-connect-eap-acr-values-1_0.html +viii:[https://openid.net/specs/openid-connect-eap-acr-values-1\_0.html](https://openid.net/specs/openid-connect-eap-acr-values-1_0.html) -ix:https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims +ix:[https://openid.net/specs/openid-connect-core-1\_0.html\#StandardClaims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) -x:https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims +x:[https://openid.net/specs/openid-connect-core-1\_0.html\#ScopeClaims](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) -xi:https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest +xi:[https://openid.net/specs/openid-connect-core-1\_0.html\#AuthRequest](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) -xii:https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication +xii:[https://openid.net/specs/openid-connect-core-1\_0.html\#ClientAuthentication](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) -xiii:https://datatracker.ietf.org/doc/html/rfc7519#section-7.2 +xiii:[https://datatracker.ietf.org/doc/html/rfc7519\#section-7.2](https://datatracker.ietf.org/doc/html/rfc7519#section-7.2) diff --git a/docs/modernidentity/ChapterSix.md b/docs/modernidentity/chaptersix.md similarity index 80% rename from docs/modernidentity/ChapterSix.md rename to docs/modernidentity/chaptersix.md index fa89773..8e8cad8 100644 --- a/docs/modernidentity/ChapterSix.md +++ b/docs/modernidentity/chaptersix.md @@ -16,15 +16,13 @@ OAuth 2与OIDC的诞生,解决了这一问题,他是保护API的基础。OAu 接下来,我们通过例子来详细了解OAuth的原理。 - - ## API访问授权 应用程序可能需要代表用户调用API,以访问用户拥有的内容,或者应用程序自身拥有所需的内容,则需要代表自己调用API。通过下图 6-1示例的场景说明这两种情况。 ![6-1 API AuthZ.jpg](https://i.loli.net/2021/07/16/xK9MLrFPnvwWQh8.jpg) -
6-1授权示例
+ 6-1授权示例 在这个场景中,写作应用程序是一个专门的编辑器,帮助用户撰写和编辑文章。它调用两个API,它们都属于不同的组织。第一个是素材库,它为文章提供有效的素材。第二个是文档服务,提供文档存储服务。还有第一个移动应用程序,它调用文档服务来提供从用户的移动设备访问文档的权限。 @@ -52,19 +50,17 @@ OAuth 2与OIDC的诞生,解决了这一问题,他是保护API的基础。OAu ![6-2 OAuth Actors.jpg](https://i.loli.net/2021/07/16/pUbtDSglOx5zyNq.jpg) -
6-2 OAuth 角色
- - +6-2 OAuth 角色 ### 公共与非公开的客户端 OAuth2.0定义了两种客户端类型: * 公共客户端 – 一种主要在用户侧设备上运营的程序,如(手机APP应用程序)或客户端浏览器中执行的应用程序,它不能安全地存储秘密。 - * 非公开客户端 – 在受保护的服务器上运行的一种应用程序,它可以安全地存储机密,可以一种安全的机制向授权服务器进行身份验证,并存储授权服务器返回的机密。 ### 客户端类型 + OAuth 2.0基于应用程序拓扑定义了三个概要的类型: * Web应用程序 – 在受保护的后端服务器上执行代码的机密客户端。服务器可以安全地存储客户端进行身份验证所需的任何机密以及从授权服务器接收的任何令牌。 @@ -78,9 +74,7 @@ OAuth 2.0基于应用程序拓扑定义了三个概要的类型: 在OAuth中,定义了两种令牌和一个授权码,换句话说,就是具有不同用途的令牌和授权码: * 授权码 – 返回给应用程序的一种中间的不透明一次性代码,用于获取访问令牌和可选的刷新令牌。每个授权码使用一次。 - * 访问令牌:应用程序用来访问API的令牌。 它表示应用程序调用API的授权,并且有一个过期时间 - * 刷新令牌:一种可选令牌,当先前的访问令牌过期时,应用程序可以使用它来请求新的访问令牌。 把访问令牌想象成一个会话,它是在您登录到网站时为您创建的。只要该会话有效,您就可以继续与网站进行交互,而无需再次登录。一旦该会话过期,您可以通过密码再次登录来获取新会话。在这种类比中,刷新令牌有点类似于密码。还有,客户端需要确保刷新令牌的安全。它应该以一种安全的方式进行凭据存储。 @@ -112,18 +106,13 @@ OAuth 2.0 授权框架规范定义了四种方法,应用程序通过这些方 第二次请求发生在应用获取到授权码后,应用向授权服务器的令牌端点发送获取令牌请求,在此请求中,包含了授权码和授权码验证信息,以获取访问令牌。授权服务器向该应用颁发授权令牌(AccessToken),返回访问令牌进行响应 ,应用就可以用此令牌来调用资源服务的API。图 6-3显示了步骤的顺序。 - - ![6-3 OAuth Process.jpg](https://i.loli.net/2021/07/16/Bs6kSKYQmJhlg1c.jpg) -
6-3 OAuth Code Flow
+6-3 OAuth Code Flow 1. 用户向应用发起请求 - 2. 应用向OAuth服务器发出一个请求,发送一个基本的授权范围,该请求通过浏览器进行重定向。 - 3. OAuth授权服务器接下来将向用户发起身份验证,和征求用户的授权同意。 - 4. 用户通过输入凭据进行身份验证,并且对请求的权限进行授权,该授权将影响令牌中包含影响作用域的信息。 5. 授权服务器生成本次授权请求的授权码,然后将此授权码包含在重定向请求中,通过浏览器将授权码返回给应用。 6. 应用向授权服务器的令牌端点发送获取令牌请求,在此请求中,包含了授权码,以获取访问令牌。 @@ -134,8 +123,7 @@ OAuth 2.0 授权框架规范定义了四种方法,应用程序通过这些方 第一个(授权)请求将用户重定向到授权服务器,以便授权服务器可以与用户交互。 -第二个(授权)请求可以由应用程序的后端直接向授权服务器的令牌端点发出。这是假定非公开的客户端(应用后段)能够安全地管理认证秘密,因而应用程序后端可以向授权服务器提供秘密并进行自身认证,并自认证成功后获取访问令牌。这还意味着可以将带有访问令牌的响应传递到应用程序后端,这将使应用可以在后续进行 API调用。这种设计的另外一个优点是令牌可以通过安全的反向通道响应返回。 -不过, 虽然这种方式开始只针对非公开的客户端进行了优化,但通过添加PKCE的方式,使得公共客户端也可以使用这种授权码流程。 +第二个(授权)请求可以由应用程序的后端直接向授权服务器的令牌端点发出。这是假定非公开的客户端(应用后段)能够安全地管理认证秘密,因而应用程序后端可以向授权服务器提供秘密并进行自身认证,并自认证成功后获取访问令牌。这还意味着可以将带有访问令牌的响应传递到应用程序后端,这将使应用可以在后续进行 API调用。这种设计的另外一个优点是令牌可以通过安全的反向通道响应返回。 不过, 虽然这种方式开始只针对非公开的客户端进行了优化,但通过添加PKCE的方式,使得公共客户端也可以使用这种授权码流程。 #### 授权码 Authorization Code+PKCE @@ -145,15 +133,13 @@ OAuth 2.0 授权框架规范定义了四种方法,应用程序通过这些方 当该流程进行到步骤6时,应用向授权服务器的令牌端点请求获取访问令牌时,需要在请求中将授权码以及编码验证码同时发送到授权服务器。授权服务器使用在授权请求中接收的转换方法对编码验证码进行转换,与第二步随授权请求发送的编码挑战码进行质询匹配。这使授权服务器能够辨别是否时恶意应用程序在试图使用被盗的授权代码。只有合法的应用程序才知道代码验证器要第6步中将需要传递的编码验证码。 - - PKCE规范列出了可用于从编码验证码派生代码挑战码的两种转换方法,即“plain”和“S256”。使用“plain”方法,代码挑战码和验证码是相同的,因此没有针对有可能被窃取的代码挑战码的保护。使用PKCE授权码的应用程序应该使用S256转换方法,该方法使用代码验证器的base64url编码的SHA256散列来保护它。 #### 授权请求 下面是一个应用基于PKCE模式向授权服务请求API授权的示例。 它将被定向到授权服务器的授权端点。 -``` +```text GET /authorize? response_type=code & client_id= @@ -163,31 +149,29 @@ response_type=code & resource= & code_challenge= & code_challenge_method=S256 HTTP/1.1 -Host: authorizationserver.com` +Host: authorizationserver.com` ``` -**表 6-1.** *授权请求的参数说明* +**表 6-1.** _授权请求的参数说明_ -| 参数 | 说明 | -| --------------------- | ------------------------------------------------------------ | -| response_type | 指示OAuth 2.0授权类型。“code”用于指示该流程为授权码流程。 | -| client_id | 应用程序的标识符,在向授权服务器注册时分配。 | -| state | 一个不可猜测的字符串,对于每个调用都是唯一的,对授权服务器来说是不透明的,客户端使用它来跟踪相应请求和响应之间的状态,以降低CSRF攻击的风险。它应该包含一个将请求与用户会话相关联的值。这可以通过将会话cookie或其他会话标识符的散列与附加的每个请求唯一组件连接起来来实现。当收到响应时,客户机应确保响应中的状态参数与从同一浏览器发送的请求的状态参数匹配。 | -| scope | 本次请求授予权限的范围。例如:“read:documents” | -| redirect_uri | 指示应用程序的回调URL,授权服务器将其带有授权代码的响应发送到此URL。 | -| resource | 在请求访问令牌的授权服务器上注册的特定API的标识符。此参数在中定义 oauth2.0扩展当中,资源指示符有些实现可能会使用其他名称,例如“audience.”,主要用于具有自定义api的部署中。如果具有多个API服务,用于区分,否则不需要此参数。 | -| code_challenge | PKCE挑战码,由PKCE编码验证码派生 | -| code_challenge_method | PKCE中指示生成挑战码的派生方法。 “S256” or “plain.” | +| 参数 | 说明 | +| :--- | :--- | +| response\_type | 指示OAuth 2.0授权类型。“code”用于指示该流程为授权码流程。 | +| client\_id | 应用程序的标识符,在向授权服务器注册时分配。 | +| state | 一个不可猜测的字符串,对于每个调用都是唯一的,对授权服务器来说是不透明的,客户端使用它来跟踪相应请求和响应之间的状态,以降低CSRF攻击的风险。它应该包含一个将请求与用户会话相关联的值。这可以通过将会话cookie或其他会话标识符的散列与附加的每个请求唯一组件连接起来来实现。当收到响应时,客户机应确保响应中的状态参数与从同一浏览器发送的请求的状态参数匹配。 | +| scope | 本次请求授予权限的范围。例如:“read:documents” | +| redirect\_uri | 指示应用程序的回调URL,授权服务器将其带有授权代码的响应发送到此URL。 | +| resource | 在请求访问令牌的授权服务器上注册的特定API的标识符。此参数在中定义 oauth2.0扩展当中,资源指示符有些实现可能会使用其他名称,例如“audience.”,主要用于具有自定义api的部署中。如果具有多个API服务,用于区分,否则不需要此参数。 | +| code\_challenge | PKCE挑战码,由PKCE编码验证码派生 | +| code\_challenge\_method | PKCE中指示生成挑战码的派生方法。 “S256” or “plain.” | 注:“resource”参数不在原始OAuth2.0规范中,刚开始,授权服务器被编写来处理多个API的请求。后来为了可以支持特定API的请求,额外的参数用来指示授权请求的特定API,该参数可以称为“resource”或“audience”。 - #### 授权响应 -授权服务器向应用程序的回调地址发送如下响应,回调地址在授权请求的redirect_uri参数中指定。 - +授权服务器向应用程序的回调地址发送如下响应,回调地址在授权请求的redirect\_uri参数中指定。 -``` +```text HTTP/1.1 302 Found Location: https://clientapplication.com/callback? @@ -197,18 +181,18 @@ code= & state= ``` -**表 6-2.** *授权响应的参数说明* +**表 6-2.** _授权响应的参数说明_ -| 参数 | 说明 | -| ----- | ------------------------------------------------------------ | -| code | 应用程序用于请求访问令牌的授权码。 | +| 参数 | 说明 | +| :--- | :--- | +| code | 应用程序用于请求访问令牌的授权码。 | | state | 在授权请求中发送的未修改的state值。应用程序必须验证响应中的状态值是否与初始请求发送的状态值匹配。 | #### 请求Token令牌 在接收到授权码之后,应用程序在对授权服务器的令牌端点发起的第二次请求中使用授权码来获取访问令牌。 -``` +```text POST /token HTTP/1.1 Host: authorizationserver.com @@ -226,19 +210,19 @@ grant_type=authorization_code & redirect_uri=<*callback URI*> ``` -**表 6-3.** *请求授权令牌中的参数说明* +**表 6-3.** _请求授权令牌中的参数说明_ -| 参数 | 说明 | -| ------------- | ------------------------------------------------------------ | -| grant_type | 指示OAuth 2.0授权类型,指定为“code”用于指示该流程为授权码流程。 | -| code | 授权响应中收到的授权码 | -| client_id | 应用程序的标识符,在向授权服务器注册时分配。 | -| code_verifier | 为了派生代码挑战码而产生的PKCE的验证码值。它应该是一个长度介于43到128个字符(含)之间的不可使用的加密随机字符串,使用字符A–Z、A–Z、0–9、“-”、“.”、“”和“~” | -| redirect_uri | 授权服务器响应的回调URI。 应该与授权请求中传递给授权端点的重定向uri的值相匹配 | +| 参数 | 说明 | +| :--- | :--- | +| grant\_type | 指示OAuth 2.0授权类型,指定为“code”用于指示该流程为授权码流程。 | +| code | 授权响应中收到的授权码 | +| client\_id | 应用程序的标识符,在向授权服务器注册时分配。 | +| code\_verifier | 为了派生代码挑战码而产生的PKCE的验证码值。它应该是一个长度介于43到128个字符(含)之间的不可使用的加密随机字符串,使用字符A–Z、A–Z、0–9、“-”、“.”、“”和“~” | +| redirect\_uri | 授权服务器响应的回调URI。 应该与授权请求中传递给授权端点的重定向uri的值相匹配 | 来自令牌终结点的响应将示例以下内容类似: -``` +```text HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 @@ -258,51 +242,43 @@ Pragma: no-cache "refresh_token":"<*refresh_token*>" } - ``` - - 响应参数如表 6-4所示。 -**表 6-4.** *令牌响应中的参数说明* +**表 6-4.** _令牌响应中的参数说明_ -| 参数 | 说明 | -| ------------- | ------------------------------------------------------------ | -| access_token | 用于调用API的访问令牌。 不同的授权服务器可能对访问令牌使用不同的格式。 | -| token_type | 颁发的令牌类型。 | -| expires_in | 令牌的有效期 | -| refresh_token | 刷新令牌 是可选的。 是否返回刷新令牌由授权服务器自行决定。有关更多信息,请参阅本章后面的刷新令牌部分。 | +| 参数 | 说明 | +| :--- | :--- | +| access\_token | 用于调用API的访问令牌。 不同的授权服务器可能对访问令牌使用不同的格式。 | +| token\_type | 颁发的令牌类型。 | +| expires\_in | 令牌的有效期 | +| refresh\_token | 刷新令牌 是可选的。 是否返回刷新令牌由授权服务器自行决定。有关更多信息,请参阅本章后面的刷新令牌部分。 | -### 隐式授权 Implicit Grant +### 隐式授权 Implicit Grant OAuth2.0定义了一个隐式授权类型,该类型针对公共客户机(如单页应用程序)进行了优化。使用此授予类型可在一个请求中返回对应用程序的访问令牌。它是在浏览器中不广泛支持CORS(Cross-Origin Resource Sharing,跨源资源共享)标准的时候设计的,它们只能调用加载页面的域,这意味这类单页应用它们不能调用授权服务器的令牌端点。为了弥补这一限制,隐式授权类型让授权服务器通过使用URL散列片段在重定向中向应用程序返回令牌来响应授权请求。隐式授权类型的交互如图所示 6-4. ![6-4 OAuth Implict Process.jpg](https://i.loli.net/2021/07/16/mBcxHjECJzOhdG7.jpg) - - -
6-4 隐式授权流程
+6-4 隐式授权流程 1. 用户向应用发起请求 - 2. 应用向OAuth服务器发出一个请求,发送一个基本的授权范围,该请求通过浏览器进行重定向。 - 3. OAuth授权服务器接下来将向用户发起身份验证,和征求用户的授权同意。 - 4. 用户通过输入凭据进行身份验证,并且对请求的权限进行授权,该授权将影响令牌中包含影响作用域的信息。 5. 授权服务器生成本次授权请求的授权令牌(AccessToken),然后将此令牌包含在重定向请求中,通过浏览器将授权令牌(AccessToken)返回给应用。 6. 应用使用此令牌来调用资源服务的API。 自从OAuth2.0规范最初发布以来,CORS已经得到了大多数浏览器的支持。因此,最初为了这个目的而设计隐式授权类型就不再需要。此外,在URL散列片段中返回访问令牌会使访问令牌暴露于通过浏览器历史或referer头,存在的潜在泄漏风险。因此,对于需要访问令牌的单页应用程序,不再建议使用URL哈希片段中返回访问令牌的隐式授予类型,而应改用授权码(PKCE)授予类型。 -还应该注意的是,在原始Oauth2.0规范发布之后,Oauth2.0多响应类型编码实践(Multiple Response Type Encoding Practices s)规范为授权请求定义了一个新的“Response_mode”参数,使应用程序能够请求以新的方式返回授权服务器响应。随后的规范定义了新的响应机制——Form Post Response Mode,将响应参数编码为HTML表单,该表单通过HTTP-Post发送到应用程序。OAuth 2.0 Web消息响应模式有一个规范草案,它利用HTML5Web消息传递将授权响应返回给应用程序。具有替代隐式授权类型响应的模式,为应用程序提供了新的选项,可以缓解与默认响应模式相关的问题。 +还应该注意的是,在原始Oauth2.0规范发布之后,Oauth2.0多响应类型编码实践(Multiple Response Type Encoding Practices s)规范为授权请求定义了一个新的“Response\_mode”参数,使应用程序能够请求以新的方式返回授权服务器响应。随后的规范定义了新的响应机制——Form Post Response Mode,将响应参数编码为HTML表单,该表单通过HTTP-Post发送到应用程序。OAuth 2.0 Web消息响应模式有一个规范草案,它利用HTML5Web消息传递将授权响应返回给应用程序。具有替代隐式授权类型响应的模式,为应用程序提供了新的选项,可以缓解与默认响应模式相关的问题。 #### 授权请求 -隐式授权类型的授权请求示例如下所示,其参数与前面的授权类型类似,但响应类型为“token” ,用于指示使用隐式授予类型,以及响应模式设置response_mode=form_post模式: +隐式授权类型的授权请求示例如下所示,其参数与前面的授权类型类似,但响应类型为“token” ,用于指示使用隐式授予类型,以及响应模式设置response\_mode=form\_post模式: -``` +```text GET /authorize? response_type=token @@ -319,27 +295,23 @@ response_type=token & state=<*state*> HTTP/1.1 -Host: authorizationserver.com +Host: authorizationserver.com ``` -隐式授权类型,如果使用默认的响应模式。则成功授权请求响应将访问令牌、令牌类型、令牌过期和状态值通过referer头重定向回应用程序的重定向URI,URL片段中的和浏览器历史会暴露这些敏感的信息。 使用form_post 模式的请求响应,将使这些值以HTML形式编码的响应发布到重定向uri,避免URL片段暴露。 +隐式授权类型,如果使用默认的响应模式。则成功授权请求响应将访问令牌、令牌类型、令牌过期和状态值通过referer头重定向回应用程序的重定向URI,URL片段中的和浏览器历史会暴露这些敏感的信息。 使用form\_post 模式的请求响应,将使这些值以HTML形式编码的响应发布到重定向uri,避免URL片段暴露。 ### 资源所有者密码凭证模式 -资源所有者密码凭据授予类型支持这样的情况:应用程序被信任来处理最终用户凭据,并且不可能有其他授予类型。对于这种授予类型,应用程序直接收集用户的凭据,而不是将用户重定向到授权服务器。应用程序将收集的凭据传递给授权服务器进行验证,作为其获取访问令牌请求的一部分。 -不鼓励使用此授予类型,因为它会向应用程序公开用户的凭据。它被用于用于遗留的嵌入式登录用户迁移场景。在这两种场景下,应用程序中的漏洞都会损害凭据。此外,此授予类型不涉及用户同意步骤,因此应用程序可以使用用户的凭据请求其希望的任何访问,用户无法防止应用滥用其凭据。 -因此,这种授权类型主要推荐用于用户迁移的用例。如果需要使用不兼容的密码散列将用户从一个标识存储库迁移到另一个标识存储库,则新系统可以提示用户输入其凭据,使用资源所有者密码授权来根据旧系统对其进行验证,如果有效,从旧系统检索用户配置文件,并将其和凭据存储在新系统中。这可以避免在迁移身份信息时进行大规模强制密码重置的必要性。如果使用此授予类型,则客户端应在获得访问令牌后立即丢弃用户凭据,以减少凭据受损的可能性。 +资源所有者密码凭据授予类型支持这样的情况:应用程序被信任来处理最终用户凭据,并且不可能有其他授予类型。对于这种授予类型,应用程序直接收集用户的凭据,而不是将用户重定向到授权服务器。应用程序将收集的凭据传递给授权服务器进行验证,作为其获取访问令牌请求的一部分。 不鼓励使用此授予类型,因为它会向应用程序公开用户的凭据。它被用于用于遗留的嵌入式登录用户迁移场景。在这两种场景下,应用程序中的漏洞都会损害凭据。此外,此授予类型不涉及用户同意步骤,因此应用程序可以使用用户的凭据请求其希望的任何访问,用户无法防止应用滥用其凭据。 因此,这种授权类型主要推荐用于用户迁移的用例。如果需要使用不兼容的密码散列将用户从一个标识存储库迁移到另一个标识存储库,则新系统可以提示用户输入其凭据,使用资源所有者密码授权来根据旧系统对其进行验证,如果有效,从旧系统检索用户配置文件,并将其和凭据存储在新系统中。这可以避免在迁移身份信息时进行大规模强制密码重置的必要性。如果使用此授予类型,则客户端应在获得访问令牌后立即丢弃用户凭据,以减少凭据受损的可能性。 资源所有者密码授予的交互类型如图6-5所示: ![6-5 OAuth RO Credentials Grant.jpg](https://i.loli.net/2021/07/16/kCBi7gUfpvdasVu.jpg) -
6-5 资源所有者密码凭证
+6-5 资源所有者密码凭证 1. 用户向应用发起请求 - 2. 应用直接向用户发起身份验证。 - 3. 用户将输入凭据提供给应用。 4. 应用将用户凭据传递给授权服务器,并请求令牌。 5. 授权服务器向该应用颁发授权令牌(AccessToken),刷新令牌(可选)。 @@ -349,9 +321,9 @@ Host: authorizationserver.com #### 授权请求 -资源所有者密码授予类型的令牌请求示例如下所示, 参数与之前的请求类型相似,但授予类型使用“grant_type=password”的类型,以及包含从用户处收集的用户名和密码。应用程序通过获得的用户凭据作为应用程序的授权, 并用于从令牌端点请求访问令牌。 +资源所有者密码授予类型的令牌请求示例如下所示, 参数与之前的请求类型相似,但授予类型使用“grant\_type=password”的类型,以及包含从用户处收集的用户名和密码。应用程序通过获得的用户凭据作为应用程序的授权, 并用于从令牌端点请求访问令牌。 -``` +```text POST /token HTTP/1.1 Host: authorizationserver.com @@ -379,20 +351,19 @@ grant_type=password ![6-6 OAuth Crediential Grant.jpg](https://i.loli.net/2021/07/16/YvfaWQzOU9wDiy5.jpg) -
6-6 客户端凭据流程
+ 6-6 客户端凭据流程 1. 应用程序将包括应用程序凭据的授权请求发送到授权服务器。 - 2. 授权服务器验证凭据并使用访问令牌进行响应。 3. 应用程序使用此令牌来调用资源服务的API。 -此流不需要最终用户与授权服务器交互。应用只需要通过自己的凭据去获取授权,该凭据由应用程序向授权服务器注册时获得,包含客户机Client_ID和Client_Secret。应用使用该凭据从令牌端点请求访问令牌。 +此流不需要最终用户与授权服务器交互。应用只需要通过自己的凭据去获取授权,该凭据由应用程序向授权服务器注册时获得,包含客户机Client\_ID和Client\_Secret。应用使用该凭据从令牌端点请求访问令牌。 #### 授权请求 -下面是客户端凭据授予类型的示例令牌请求,其参数定义与之前授予类型的类似, 但grant_type设置为“client_credentials”。在本例中,应用程序使用在授权服务器上注册的客户端Cilent_ID和Secret进行身份验证。 +下面是客户端凭据授予类型的示例令牌请求,其参数定义与之前授予类型的类似, 但grant\_type设置为“client\_credentials”。在本例中,应用程序使用在授权服务器上注册的客户端Cilent\_ID和Secret进行身份验证。 -``` +```text POST /token HTTP/1.1 Host: authorizationserver.com @@ -414,7 +385,7 @@ grant_type=client_credentials 一旦应用程序接收到访问令牌,它就会调用资源服务器并传递访问令牌。虽然不同的API的调用会有差异,但典型的方法是使用HTTP“Authorization”请求头字段,在授权头中使用承载身份验证令牌类型,并使用访问令牌,如以下代码段所示: -``` +```text GET /api-endpoint HTTP/1.1 Host: api-server.com @@ -432,11 +403,11 @@ OAuth 2.0中访问令牌具有有效期,当访问令牌过期时,应用程 刷新令牌为传统Web应用程序和本机应用程序获取新的访问令牌提供了一种方便的方法。 这有助于使用持续时间较短的访问令牌,从而将访问令牌受损的风险降至最低。一旦访问令牌过期,就自动刷新它可能很有诱惑力,但是为了保持最小特权的原则,最好只在需要时刷新访问令牌,而不是总是将当前的访问令牌保留在手边。 同样,应用程序必须安全地存储刷新令牌,因为它是敏感凭据。 - OAuth2.0规范没有为应用程序提供请求刷新令牌的机制,这让授权服务器自行决定是否发布令牌。因此,刷新令牌的处理可能会因各个授权服务器而异。有些自动发出刷新令牌,有些则希望应用程序显式请求刷新令牌(下一章将介绍的OIDC规范包括一种机制,用于应用程序为一个特定的用例请求刷新令牌。)撤销访问令牌的能力不是OAuth2.0规范中的强制功能,因此一些授权服务器可能不支持它。您选择的授权服务器的文档应解释具体实现的详细信息。 +OAuth2.0规范没有为应用程序提供请求刷新令牌的机制,这让授权服务器自行决定是否发布令牌。因此,刷新令牌的处理可能会因各个授权服务器而异。有些自动发出刷新令牌,有些则希望应用程序显式请求刷新令牌\(下一章将介绍的OIDC规范包括一种机制,用于应用程序为一个特定的用例请求刷新令牌。)撤销访问令牌的能力不是OAuth2.0规范中的强制功能,因此一些授权服务器可能不支持它。您选择的授权服务器的文档应解释具体实现的详细信息。 下面的示例中显示了对授权服务器的令牌端点的示例调用,以请求新的访问令牌。客户端必须对请求进行身份验证。 -``` +```text POST /token HTTP/1.1 Host: authorizationserver.com @@ -468,9 +439,8 @@ OAuth2.0协议使应用程序能够获得代表用户或自己调用API的授权 * 作用域用于控制应用程序在调用API时的访问权限。 * PKCE的授权码模式可以用于传统的Web应用程序、公共应用程序以及本机应用程序的授权使用。 - -* 不建议使用OAuth2.0隐式授予类型获取具有默认响应模式的访问令牌,因为它会使访问令牌暴露于潜在的危害。需要使用form_post方式。 +* 不建议使用OAuth2.0隐式授予类型获取具有默认响应模式的访问令牌,因为它会使访问令牌暴露于潜在的危害。需要使用form\_post方式。 * OAuth2.0资源所有者密码授予类型最好仅限于旧式用户迁移情况,因为它向应用程序公开用户凭据。 * 客户端凭据授予类型用于应用程序拥有请求的资源的API调用。 - * 刷新令牌用于在旧的访问令牌过期时获取新的访问令牌。OAuth2.0威胁模型和安全注意事项文档提出了刷新令牌轮换的概念,以检测刷新令牌是否被盗。要求授权服务器在每次访问令牌续订请求时返回一个新的刷新令牌。 + diff --git a/docs/modernidentity/ChapterTen.md b/docs/modernidentity/chapterten.md similarity index 90% rename from docs/modernidentity/ChapterTen.md rename to docs/modernidentity/chapterten.md index 0691314..35c2647 100644 --- a/docs/modernidentity/ChapterTen.md +++ b/docs/modernidentity/chapterten.md @@ -36,13 +36,10 @@ ## 多处会话 -一个用户可能在不同的解决方案组件之间有多个会话。用户可以在一个或多个应用程序中拥有会话。如果应用程序将身份验证委托给身份提供程序,则该身份提供程序可以为用户提供会话。如果应用程序将身份验证委托给身份验证代理,而身份验证代理又将身份验证委托给远程身份提供商,例如社交身份提供商或企业身份提供商,则可能存在三个架构层,其中都会存在会话。 -图10-2显示了三种不同的体系结构模型以及可能存在认证会话的位置。 +一个用户可能在不同的解决方案组件之间有多个会话。用户可以在一个或多个应用程序中拥有会话。如果应用程序将身份验证委托给身份提供程序,则该身份提供程序可以为用户提供会话。如果应用程序将身份验证委托给身份验证代理,而身份验证代理又将身份验证委托给远程身份提供商,例如社交身份提供商或企业身份提供商,则可能存在三个架构层,其中都会存在会话。 图10-2显示了三种不同的体系结构模型以及可能存在认证会话的位置。 ![10-1 Sessions Type.jpg](https://i.loli.net/2021/09/08/fSRA3zbr9ijXgsP.jpg) - - ## 会话持续时间 为用户建立的每个会话可以在不同的时间因各种原因终止。除了用户主动退出会话,会话会被终止。其他情况下,如会话的时间如果超过最大的时长限制,不管用户是否活动,会话可能会被终止。用户如果长时间没有活动,会话的空闲时间超过了会话空闲时间限制,会话也会被终止。 @@ -65,7 +62,7 @@ 当用户的应用程序会话到期时,应用程序可能希望用户能够续订会话。它可以通过将用户重定向回身份提供者来实现这一点。如果用户没有有效的会话,身份提供者可以对用户进行身份验证,并根据应用程序身份验证请求中的参数将新的安全令牌返回给应用程序。如果用户的身份提供程序会话仍然有效,则用户无需重新验证,应用程序将根据用户的现有会话接收新的安全令牌。然后,应用程序可以在续订用户的应用程序会话时使用新安全令牌中的信息。 -应用程序可以在身份验证请求中使用参数来抑制或强制活动身份验证。例如,如果自用户上次主动身份验证以来经过了一定的时间,则可能需要进行重新身份验证。在OIDC协议中,可以将可选的“prompt”参数添加到身份验证请求中,以强制或禁止OpenID提供者上的弹出或者禁止身份验证。可选的“max_age”参数可用于控制用户在通过认证后的最长使用时间,使用max_age的应用程序仍应检查ID令牌中的auth_time声明,以确保遵循了请求的max_age。如果OpenID提供程序具有相对较长的最大或空闲时间,但是特定应用程序希望更加频繁地进行身份验证,那么使用max_age和auth_time就会非常的实用。使用SAML2.0,应用程序可以使用身份验证请求的“ForceAuthn”属性强制身份提供程序主动对用户进行身份验证。此类身份验证请求参数为应用程序提供了一定程度的控制,以控制当用户重定向到身份提供程序时,是否对其进行主动重新身份验证。 +应用程序可以在身份验证请求中使用参数来抑制或强制活动身份验证。例如,如果自用户上次主动身份验证以来经过了一定的时间,则可能需要进行重新身份验证。在OIDC协议中,可以将可选的“prompt”参数添加到身份验证请求中,以强制或禁止OpenID提供者上的弹出或者禁止身份验证。可选的“max\_age”参数可用于控制用户在通过认证后的最长使用时间,使用max\_age的应用程序仍应检查ID令牌中的auth\_time声明,以确保遵循了请求的max\_age。如果OpenID提供程序具有相对较长的最大或空闲时间,但是特定应用程序希望更加频繁地进行身份验证,那么使用max\_age和auth\_time就会非常的实用。使用SAML2.0,应用程序可以使用身份验证请求的“ForceAuthn”属性强制身份提供程序主动对用户进行身份验证。此类身份验证请求参数为应用程序提供了一定程度的控制,以控制当用户重定向到身份提供程序时,是否对其进行主动重新身份验证。 除了通过重定向的方式,为了改善用户体验。身份提供者可以支持检查用户会话状态的替代方法。如果用户在身份提供者处具有有效会话,则允许在不需要浏览器重定向的情况下续订应用程序会话。如果用户的身份提供程序会话不再有效,则可以重定向该用户以续订身份提供程序会话。 @@ -73,8 +70,7 @@ 除了更新会话外,应用程序可能还需要定期更新安全令牌。应用程序可能已收到ID令牌,也可能已收到用于调用API的访问令牌。应用程序可能希望定期请求新的ID令牌,以确保其对经过身份验证的用户具有最新的声明。应用程序以前请求的访问令牌已经过期,可能希望请求一个新的访问令牌,因为它需要调用一个API。在许多情况下,尤其是对于公共客户机,发行到期时间较短的访问令牌并在需要时更新令牌被认为是一种最佳做法,因此在用户会话存在的整个过程中都可能需要新令牌。 -,在应用程序会话期间,根据应用程序的类型,应用程序可以使用不同的机制更新ID令牌或访问令牌。传统Web应用程序和本机应用程序可能能够获取刷新令牌以用于续订ID令牌和/或访问令牌,但是,这样做并不是必须的。**使用刷新令牌续订令牌可避免中断用户体验,但是通过后端的刷新令牌请求通道请求ID或访问令牌,可能不会更新身份提供程序的会话cookie,从而导致更快的空闲超时**。 -作为公共客户端实现的单页应用(SPA)程序,无法安全地存储和处理刷新令牌,因此必须使用不依赖刷新令牌的方法,除非其授权服务器对刷新令牌的泄漏风险采取了安全措施(如使用刷新令牌轮换或具有使用约束条件的刷新令牌)。不依赖刷新令牌的应用程序接收刷新令牌可以在需要新令牌时将用户重定向到OpenID提供程序。如果用户具有有效会话,则应用程序将接收新令牌。如果用户没有有效会话,请求将根据需要触发身份验证和同意。即使是使用刷新令牌的应用程序,也可能希望定期使用重定向方法来更新身份提供程序的会话cookie和空闲超时。 +,在应用程序会话期间,根据应用程序的类型,应用程序可以使用不同的机制更新ID令牌或访问令牌。传统Web应用程序和本机应用程序可能能够获取刷新令牌以用于续订ID令牌和/或访问令牌,但是,这样做并不是必须的。**使用刷新令牌续订令牌可避免中断用户体验,但是通过后端的刷新令牌请求通道请求ID或访问令牌,可能不会更新身份提供程序的会话cookie,从而导致更快的空闲超时**。 作为公共客户端实现的单页应用(SPA)程序,无法安全地存储和处理刷新令牌,因此必须使用不依赖刷新令牌的方法,除非其授权服务器对刷新令牌的泄漏风险采取了安全措施(如使用刷新令牌轮换或具有使用约束条件的刷新令牌)。不依赖刷新令牌的应用程序接收刷新令牌可以在需要新令牌时将用户重定向到OpenID提供程序。如果用户具有有效会话,则应用程序将接收新令牌。如果用户没有有效会话,请求将根据需要触发身份验证和同意。即使是使用刷新令牌的应用程序,也可能希望定期使用重定向方法来更新身份提供程序的会话cookie和空闲超时。 然而,将用户重定向到OpenID提供方并返回会带来用户体验的挑战,有可能会中断用户体验。对于单页应用程序,这可能会导致用户的工作丢失,除非应用程序保存用户准备提交的数据,并在从OpenID提供方返回后恢复它。**一个改进是在应用程序中使用隐藏的iframe进行重定向,并将“prompt”参数设置为“none”,以避免中断用户体验。如果用户具有有效会话,则应用程序将接收新令牌。如果没有,应用程序将收到错误响应,并且可以再次重定向用户,而无需使用“prompt=none”选项触发身份验证。OpenID提供商应在提供SDK中包含相关的设计,使应用程序更容易执行此操作。** @@ -85,3 +81,4 @@ ## 本章重点回顾 应用程序在用户与应用程序交互期间为用户维护会话。如果应用程序将身份验证委托给外部身份提供程序,则在解决方案体系结构的不同层上,用户可能会有多个会话。为用户维护会话的每个组件可能有一种或多种类型的会话超时。会话是实现单点登录的关键因素。 + diff --git a/docs/modernidentity/ChapterThree.md b/docs/modernidentity/chapterthree.md similarity index 81% rename from docs/modernidentity/ChapterThree.md rename to docs/modernidentity/chapterthree.md index 3542c2a..3e6f439 100644 --- a/docs/modernidentity/ChapterThree.md +++ b/docs/modernidentity/chapterthree.md @@ -12,7 +12,7 @@ 第一阶段:石器时代,各个应用使用独立的身份体系、独立进行认证和存储身份,后来基于Cookie进行内部应用的单点登录。 -第二阶段,企业内部的应用越来越多,身份管理成为企业头痛的问题。2003年为了解决企业应用的联合认证的问题,微软联合、IBM等传统互联网巨头发起了基于WS*系列标准之上构建的WS-Federation1.1版本,但这一协议因为过于重,除了得到微软的积极拥护以外,其他软件厂商响应屈指可数。因而,2005年,SAML2.0横空出式,因为其协议的相对WS-Fed更为简洁明了,更因为适逢SaaS应用的发展,得到了一种SaaS厂商的支持。这也就是为何大家看到国外的SaaS平台基本上都继承了使用SAML的这一优良传统。 +第二阶段,企业内部的应用越来越多,身份管理成为企业头痛的问题。2003年为了解决企业应用的联合认证的问题,微软联合、IBM等传统互联网巨头发起了基于WS\*系列标准之上构建的WS-Federation1.1版本,但这一协议因为过于重,除了得到微软的积极拥护以外,其他软件厂商响应屈指可数。因而,2005年,SAML2.0横空出式,因为其协议的相对WS-Fed更为简洁明了,更因为适逢SaaS应用的发展,得到了一种SaaS厂商的支持。这也就是为何大家看到国外的SaaS平台基本上都继承了使用SAML的这一优良传统。 第三阶段,随着互联网、移动互联网的相继爆发,OpenID和Oauth应运而生,并且得到广泛的应用。在2012年以前,互联网之前的应用之间的信息交互较少。一方面互联网爆发式增长,让资源相互调用的需求迅猛增长,这也促进了Oauth2.0的诞生,同时,因为Oauth2.0,也让互联网应用之间的交互更加的蓬勃。 @@ -20,13 +20,13 @@ ![3-1-ID Protocol History.jpg](https://i.loli.net/2021/07/16/k9B2tZXmljREhUg.jpg) -## 石器时代-独立存储身份 +## 石器时代-独立存储身份 在石器时代的计算机应用程序中,每个应用程序通常实现自己的身份存储库、身份验证和授权。大型企业公司通常有核心业务应用程序,如财务和库存控制系统,也许还有一些生产力应用程序。每个应用程序通常都有自己的专用数据库或其他存储,其中存储了用户身份、凭据和用户配置文件数据,每个应用程序都会提示用户登录,然后根据自己的用户配置文件存储库验证用户的凭据信息。这意味着员工可能需要为每个应用程序记住不同的用户名和密码。这也意味着,如果用户配置文件中的某个元素发生了更改,则必须在多个应用程序中进行配置文件更改。当然,如果一家公司有许多应用程序,这种情况就不会可靠地发生,因此用户配置文件数据在各个系统中总是不同步。当只有几个应用程序时,用户对数据完整性问题的恼怒和必须记住大量密码就足够糟糕了。然而,随着企业中应用程序数量的增长,让每个应用程序实现自己的筒仓式身份存储库和身份验证解决方案很快就变得难以支撑。 这种孤立的方法目前仍在许多面向消费者的场景中使用,其中用户通过提供特定于应用程序的用户名和密码进行注册。如果一个用户在多个站点上重复使用同一个密码,任何一个站点上的泄露都可能使该用户的数据在其他站点上受到威胁。如果用户为每个应用程序指定不同的密码,他们必须记住或安全地存储密码,或者依赖应用程序提供的帐户恢复过程的安全性。不管是哪种方式,消费者用户都会面临与企业用户之前经历的这种方法相同的一些不便。 -## 目录服务-集中存储身份 +## 目录服务-集中存储身份 随着时间的推移,为广泛的业务功能编写的软件越来越多。这促使人们需要一种更好的身份管理方法。许多公司实施目录服务,如LDAP或AD来存放和集中用户身份信息。目录服务针对频繁读取但很少修改的信息进行了优化,这通常适用于用户身份数据。应用程序能够使用目录服务来存储用户数据和凭据。应用程序还可以使用目录服务中的信息提示用户登录并验证输入的凭据。面向企业环境的大型现场商业业务应用程序 包括对这种方法的支持。这种集中化的方法与按应用程序的孤立方法相比有了显著的改进。 @@ -34,8 +34,6 @@ 然而,尽管目录服务有很多优点,但也有一些缺点。目录服务本身并没有为用户维护任何类型的会话。目录服务中的身份信息集中通常意味着用户只有一个用户名和密码需要记住,但是用户仍然必须在每个应用程序的登录屏幕中输入凭据,因为每个应用程序都需要收集用户凭据并使用目录服务验证它们(在没有其他技术的情况下)。除了带来不便之外,这还将用户的密码暴露给应用程序。一个应用程序中的沦陷可能会使其他应用程序处于危险之中。当所有涉及的应用程序都在可信的公司网络中时,这种情况就已经够糟了。随着公司开始使用云应用程序,向云应用程序公开目录密码被其他人拥有会带来不可接受的风险。再一次,需要一个更好的解决方案! - - ## 早期SSO服务器 几种类型的身份和访问管理(IAM)或单点登录(SSO)服务器提供了进一步的改进。早期的SSO服务器利用了目录服务中的身份信息,但在目录顶部提供了一个层维护会话以记住已通过身份验证的用户的服务。它们的工作方式多种多样,但在一种典型的方法中,应用程序可以将用户的浏览器重定向到SSO服务器,以便在那里对用户进行身份验证,并以安全的、预先确定的方式接收身份验证结果。如果用户在第一个应用程序的身份验证后不久访问了第二个应用程序,则第二个应用程序会将用户的浏览器重定向到SSO服务器,并且SSO服务器会检测到用户的现有会话,并以成功状态将其重定向回应用程序,而不会再次提示用户提供凭据。 @@ -46,7 +44,7 @@ ## 联合身份WS-Fed -WS-Federation(简称: WS-Fed )联合框架属于Web Services Security(简称: WS-Security、WSS: 针对web-service安全性方面扩展的协议标准集合) 的一部分。WS-Federation规范采用了XML以及其他Web服务标准,从而可以使开发者能够实现在不同环境下的网络安全管理及建立相互协调信赖关系的目的。 +WS-Federation(简称: WS-Fed \)联合框架属于Web Services Security\(简称: WS-Security、WSS: 针对web-service安全性方面扩展的协议标准集合\) 的一部分。WS-Federation规范采用了XML以及其他Web服务标准,从而可以使开发者能够实现在不同环境下的网络安全管理及建立相互协调信赖关系的目的。 ![3-1-WS-FED.jpg](https://i.loli.net/2021/07/16/Cyv9SudNtDYx32w.jpg) @@ -58,46 +56,33 @@ WS-Fed可用于完成从身份提供者到服务提供者的联合单点登录 而对于互联网或是SaaS服务厂商,更愿意选择SAML。 - - ## 联合身份 SAML 2.0 -SAML 2.0在2005年三月正式代替SAML 1.1成为OASIS标准。来自超过24个公司及团体的大约30人参与了SAML 2.0的创建。尤其是,自由联盟将身份联合框架规范(ID-FF)贡献给OASIS,后者成为了SAML 2.0规范的基础。 +SAML 2.0在2005年三月正式代替SAML 1.1成为OASIS标准。来自超过24个公司及团体的大约30人参与了SAML 2.0的创建。尤其是,自由联盟将身份联合框架规范(ID-FF)贡献给OASIS,后者成为了SAML 2.0规范的基础。 SaaS应用程序的爆炸式增长给身份管理带来了挑战转眼间,企业纷纷采用SaaS服务,使得IT部门对于SaaS应用的身份管理力不从心,在SaaS应用程序中通常没有管理员工身份的好方法。对于一家公司来说,很难管理其员工在SaaS系统中账户,同时,用户再次必须记住每个应用程序的密码。他们享受的跨内部应用程序的单点登录并没有扩展到外部SaaS应用程序。 -幸运的是,在2005年,发布了一个新的行业标准Saml2.0(securityassertion-Markup)。它提供了跨域和联合身份的web单点登录解决方案。这恰好适合于使用SaaS应用程序的企业, SAML 2.0为需要在SaaS应用程序中更好地控制员工身份的企业提供了一个极好的解决方案。 +幸运的是,在2005年,发布了一个新的行业标准Saml2.0(securityassertion-Markup\)。它提供了跨域和联合身份的web单点登录解决方案。这恰好适合于使用SaaS应用程序的企业, SAML 2.0为需要在SaaS应用程序中更好地控制员工身份的企业提供了一个极好的解决方案。 -SaaS应用程序可以将企业用户重定向回企业身份验证服务(称为身份提供者(IdP))进行身份验证。身份联合基于网络跨域的单点登录(SSO), 以便于减少向一个用户分发多个身份验证令牌的管理开销。用户也只需记住一个用户名/密码,而无需反复登录。无论是对内部和外部应用,Saml赋予企业一个集中控制点,如果需要,可以在企业身份提供者处快速关闭一个用户的访问权限。密码策略和多因素身份验证也可以在一个地方实现,通过这种方式,SAML2.0解决了企业的许多身份问题。 +SaaS应用程序可以将企业用户重定向回企业身份验证服务(称为身份提供者(IdP))进行身份验证。身份联合基于网络跨域的单点登录\(SSO\), 以便于减少向一个用户分发多个身份验证令牌的管理开销。用户也只需记住一个用户名/密码,而无需反复登录。无论是对内部和外部应用,Saml赋予企业一个集中控制点,如果需要,可以在企业身份提供者处快速关闭一个用户的访问权限。密码策略和多因素身份验证也可以在一个地方实现,通过这种方式,SAML2.0解决了企业的许多身份问题。 尽管被广泛采用,但是Saml2.0并不是万能的。该协议被设计为覆盖许多场景,使得配置和实现变得复杂。虽然SAML2.0在企业环境中被广泛采用,但是没有一个可行的业务模型来解决面向消费者的场景。另一个限制是saml2.0只解决了身份验证问题,而没有解决API的授权问题。应用程序正在向基于微服务、API的体系结构发展,正如通常实现的那样,SAML2.0解决了对用户进行身份验证的问题,但对API授权没有帮助。 - - ## 互联网时代OpenID SAML2.0只在面向员工的场景中采用,消费者用户仍然被迫在每个面向消费者的网站上重新注册。一个新的行业组织成立,为它所谓的“以用户为中心”的身份创建了一个解决方案,并由此产生了一个称为OpenID的协议。 OpenID是一个去中心化的网上身份认证系统。对于支持OpenID的网站,用户不需要记住像用户名和密码这样的传统验证标记。取而代之的是,他们只需要预先在一个作为OpenID身份提供者(identity provider, IdP)的网站上注册。OpenID是去中心化的,任何网站都可以使用OpenID来作为用户登录的一种方式,任何网站也都可以作为OpenID身份提供者。OpenID既解决了注册问题而又不需要依赖于中心性的网站来确认数字身份。 - 与面向组织控制的身份提供者 (Saml2.0和WS-Fed)不同,OpenID则提供了面向用户控制的消费者身份的协议。一些大型的互联网公司,Yahoo、最初的OpenID协议并没有得到广泛的应用,但它确实强调了以用户为中心的身份解决方案的必要性,并为另一个名为OpenID Connect的协议奠定了基础,我们稍后将介绍这个协议。 - - +与面向组织控制的身份提供者 (Saml2.0和WS-Fed)不同,OpenID则提供了面向用户控制的消费者身份的协议。一些大型的互联网公司,Yahoo、最初的OpenID协议并没有得到广泛的应用,但它确实强调了以用户为中心的身份解决方案的必要性,并为另一个名为OpenID Connect的协议奠定了基础,我们稍后将介绍这个协议。 ## 互联网时代Oauth2.0 在介绍OpenID Connect协议之前,我们首先要了解下授权协议[Oauth](https://www.oauth.com),它并非是一个认证的协议,而是一个授权的协议,即无法用来认证使用者的身份,而是在知道使用者身份以后来颁发对于该使用者的权限。 -OAuth开始于2006年11月,当时布莱恩·库克正在开发Twitter的OpenID实现。与此同时,社交书签网站Ma.gnolia需要一个解决方案允许使用OpenID的成员授权Dashboard访问他们的服务。这样库克、克里斯·梅西纳和来自Ma.gnolia的拉里·哈尔夫(Larry Halff)与戴维·雷科尔顿会面讨论在Twitter和Ma.gnolia API上使用OpenID进行委托授权。但他们讨论得出结论,认为OpenID没有完成API访问委托的开放标准。 -2007年4月,成立了OAuth讨论组,这个由实现者组成的小组撰写了一个开放协议的提议草案。来自Google的德维特·克林顿获悉OAuth项目后,表示他有兴趣支持这个工作。2007年7月,团队起草了最初的规范。随后,Eran Hammer-Lahav加入团队并协调了许多OAuth的稿件,创建了更为正式的规范。2007年10月, OAuth核心1.0最后的草案发布了。 -2008年11月,在明尼阿波利斯举行的互联网工程任务组第73次会议上,举行了OAuth的BoF讨论将该协议纳入IETF做进一步的规范化工作。这个会议参加的人很多,关于正式地授权在IETF设立一个OAuth工作组这一议题得到了广泛的支持。2010年4月,OAuth 1.0协议发表为[RFC 5849](https://datatracker.ietf.org/doc/html/rfc5849),一个非正式RFC。 2012年10月,OAuth 2.0发布,正式发表为[RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749)。OAuth 2.0是OAuth协议的下一版本,但不向下兼容OAuth 1.0。OAuth 2.0关注客户端开发者的简易性,同时为Web应用、桌面应用、手机和智能设备提供专门的认证流程。 -Facebook的新的Graph API只支持OAuth 2.0,Google在2011年3月也宣布Google API对OAuth 2.0的支持,越来越多的互联网应用开始支持OAuth2.0。 - - - -## 云原生 OpenID Connect (OIDC) - +OAuth开始于2006年11月,当时布莱恩·库克正在开发Twitter的OpenID实现。与此同时,社交书签网站Ma.gnolia需要一个解决方案允许使用OpenID的成员授权Dashboard访问他们的服务。这样库克、克里斯·梅西纳和来自Ma.gnolia的拉里·哈尔夫(Larry Halff)与戴维·雷科尔顿会面讨论在Twitter和Ma.gnolia API上使用OpenID进行委托授权。但他们讨论得出结论,认为OpenID没有完成API访问委托的开放标准。 2007年4月,成立了OAuth讨论组,这个由实现者组成的小组撰写了一个开放协议的提议草案。来自Google的德维特·克林顿获悉OAuth项目后,表示他有兴趣支持这个工作。2007年7月,团队起草了最初的规范。随后,Eran Hammer-Lahav加入团队并协调了许多OAuth的稿件,创建了更为正式的规范。2007年10月, OAuth核心1.0最后的草案发布了。 2008年11月,在明尼阿波利斯举行的互联网工程任务组第73次会议上,举行了OAuth的BoF讨论将该协议纳入IETF做进一步的规范化工作。这个会议参加的人很多,关于正式地授权在IETF设立一个OAuth工作组这一议题得到了广泛的支持。2010年4月,OAuth 1.0协议发表为[RFC 5849](https://datatracker.ietf.org/doc/html/rfc5849),一个非正式RFC。 2012年10月,OAuth 2.0发布,正式发表为[RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749)。OAuth 2.0是OAuth协议的下一版本,但不向下兼容OAuth 1.0。OAuth 2.0关注客户端开发者的简易性,同时为Web应用、桌面应用、手机和智能设备提供专门的认证流程。 Facebook的新的Graph API只支持OAuth 2.0,Google在2011年3月也宣布Google API对OAuth 2.0的支持,越来越多的互联网应用开始支持OAuth2.0。 +## 云原生 OpenID Connect \(OIDC\) OpenID Connect是一种基于OAuth2.0规范的互操作认证协议,旨在提供身份验证、授权服务所需的关键功能。它使用简单的REST/JSON消息流,其设计目标是“使简单的事情变得简单和复杂的事情成为可能”。与任何先前的身份协议相比,开发人员集成起来是更加容易。 @@ -129,22 +114,15 @@ OIDC为用户、应用程序开发人员和身份提供者提供了好处。网 因此,使用行业标准身份协议有四个很好的理由! - - 如果您对身份验证领域还比较陌生,那么首先学习这些协议可能会让您感到有点畏缩,并且可能会尝试自己发明一个更简单的身份验证方案。对此我们有两个词:“不要!”我们希望这本书能让你更容易理解如何使用这些协议。我们不想阻碍创新,但在认证领域的创新应该谨慎行事。你的创新最好把精力花在应用程序的核心价值主张上! - - ## 本章重点回顾 +* 身份管理、身份验证和授权方法不断发展 随着时间的推移。早期的方法通常涉及特定于应用程序的标识和凭证。 +* 身份集中使用目录服务的数据启用了单个标识和凭据,但必须输入 由一个用户进入每个应用程序(在没有其他补充技术的情况下)。 +* 单点登录服务器提供了会话管理,因此用户可以一次登录并通过一次身份验证访问同一域中的多个应用程序。 +* Saml2.0和WS-Fed提供了跨域的单点登录和联合身份。 +* OpenID提供了一个面向消费者的解决方案。 +* OAuth公司 2.0提供了一个授权应用程序调用API的解决方案。 +* OIDC在OAuth之上提供了一个层 2.0用于对用户进行身份验证,并以标准格式向应用程序返回有关已验证用户的身份断言信息。 - -- 身份管理、身份验证和授权方法不断发展 随着时间的推移。早期的方法通常涉及特定于应用程序的标识和凭证。 -- 身份集中使用目录服务的数据启用了单个标识和凭据,但必须输入 由一个用户进入每个应用程序(在没有其他补充技术的情况下)。 - -- 单点登录服务器提供了会话管理,因此用户可以一次登录并通过一次身份验证访问同一域中的多个应用程序。 -- Saml2.0和WS-Fed提供了跨域的单点登录和联合身份。 -- OpenID提供了一个面向消费者的解决方案。 - -- OAuth公司 2.0提供了一个授权应用程序调用API的解决方案。 -- OIDC在OAuth之上提供了一个层 2.0用于对用户进行身份验证,并以标准格式向应用程序返回有关已验证用户的身份断言信息。 diff --git a/docs/modernidentity/ChapterTwelve.md b/docs/modernidentity/chaptertwelve.md similarity index 77% rename from docs/modernidentity/ChapterTwelve.md rename to docs/modernidentity/chaptertwelve.md index 4801ef9..c8fc3f8 100644 --- a/docs/modernidentity/ChapterTwelve.md +++ b/docs/modernidentity/chaptertwelve.md @@ -1,4 +1,4 @@ -# 第十二章 强身份认证 +# 第十二章 强身份认证 不同的身份验证方法的认证强度并不相同。静态密码被认为是一种相对较弱的身份验证机制。目前大多数互联网的应用仍然在采用静态密码的方式来进行认证,当然,有很多涉及到更多的用户数据、用户隐私的应用、企业的数据,都在引入更强的身份验证形式。在本章中,我们将讨论静态密码的问题,以及如何引入更强的身份验证形式用于多因素身份验证和逐步增强身份验证。 @@ -14,7 +14,7 @@ 为了规避直接使用生物因素的风险,另一种方式也在推广当中,使用用户的签名进行挑战验证,将用户的私有加密密钥对安全封装在设备(如智能卡、硬件安全令牌或移动电话)中。希望认证用户的实体向认证器设备发送质询nonce,封装在设备中的密钥用于对质询nonce进行签名。对于多因素身份验证设备,用户必须输入PIN或提供生物特征因素以解锁设备,然后才能签署质询。认证实体接收来自具有已签名质询nonce的设备的消息,并使用先前注册的信息验证签名,以确定用户(受试者)是否拥有认证器设备。使用这种方法,身份验证基于拥有设备的密钥以及解锁设备的因素。 -值得注意的是,基于知识的身份验证(KBA)涉及回答安全问题,与密码具有类似的风险。答案可能被远程实体猜测或窃取,并在所有者不知情的情况下使用。可以对认证方法的强度进行分类,**其中一个示例分类方案是美国国家标准技术研究院NIST 800-63安全标准[i](https://pages.nist.gov/800-63-3/sp800-63b.html),该标准定义了三个认证者保证级别的标准。目前国内尚没有对应的标准可以参考**。 +值得注意的是,基于知识的身份验证(KBA)涉及回答安全问题,与密码具有类似的风险。答案可能被远程实体猜测或窃取,并在所有者不知情的情况下使用。可以对认证方法的强度进行分类,**其中一个示例分类方案是美国国家标准技术研究院NIST 800-63安全标准**[**i**](https://pages.nist.gov/800-63-3/sp800-63b.html)**,该标准定义了三个认证者保证级别的标准。目前国内尚没有对应的标准可以参考**。 ### MFA 多因素认证 @@ -22,10 +22,9 @@ 使用多因素身份验证,当一个因素被破解后,其他的强因素可以降低整个系统的受损风险。如果身份验证需要输入静态密码以及手机生成的一次性密码,黑客将不得不窃取用户密码并解锁手机,以模拟用户并访问其帐户。因此,需要多个因素进行身份验证可以更有力地保证身份验证人是合法的帐户所有者。 -在某些情况下,授权策略可能需要多因素身份验证。并不是所有的情况下,都会要求多因素认证,可能会降低用户体验。 -在某些,仅当检测到异常情况(例如用户试图从新设备或非典型地理位置访问)时,才可能需要多因素身份验证。但是在某些企业环境可能需要多因素身份验证才能进行远程访问,甚至有的在办公室中访问也需要进行多因素身份验证才能获得更敏感的资源。对于访问敏感内容,可能需要每次都进行多因素的认证,例如管理员对生产云服务器的访问。 +在某些情况下,授权策略可能需要多因素身份验证。并不是所有的情况下,都会要求多因素认证,可能会降低用户体验。 在某些,仅当检测到异常情况(例如用户试图从新设备或非典型地理位置访问)时,才可能需要多因素身份验证。但是在某些企业环境可能需要多因素身份验证才能进行远程访问,甚至有的在办公室中访问也需要进行多因素身份验证才能获得更敏感的资源。对于访问敏感内容,可能需要每次都进行多因素的认证,例如管理员对生产云服务器的访问。 -选择解决方案的身份验证机制时,应考虑到所涉及的应用程序和数据的敏感性以及解决方案的可用性,因为用户可能会试图规避对于特定情况来说过于繁重或被认为过于苛刻的机制。**美国国家标准技术研究院关于身份的标准NIST800-63-3第6节,特别是第6.2节[ii]( https://pages.nist.gov/800-63-3/sp800-63-3.html#sec6),展示了如何为部署的应用选择合适的认证保证级别的一个示例。(NIST特别出版物800-63B[iii](https://pages.nist.gov/800-63-3/sp800-63b.html)附带了每个认证保证级别的认证类型列表。** +选择解决方案的身份验证机制时,应考虑到所涉及的应用程序和数据的敏感性以及解决方案的可用性,因为用户可能会试图规避对于特定情况来说过于繁重或被认为过于苛刻的机制。**美国国家标准技术研究院关于身份的标准NIST800-63-3第6节,特别是第6.2节**[**ii**](https://pages.nist.gov/800-63-3/sp800-63-3.html#sec6)**,展示了如何为部署的应用选择合适的认证保证级别的一个示例。(NIST特别出版物800-63B**[**iii**](https://pages.nist.gov/800-63-3/sp800-63b.html)**附带了每个认证保证级别的认证类型列表。** ### 认证升级 @@ -39,14 +38,13 @@ ## 请求身份验证机制 -应用需要向身份提供者请求使用特定类别的身份验证机制来实现所需级别的身份验证保证。这可以通过身份验证上下文类引用来完成,身份验证上下文涉及多个因素,例如用于创建帐户的标识过程、防止凭据泄露的保护以及使用的身份验证机制。身份验证上下文类表示一组身份验证方法,身份验证上下文类引用是身份验证上下文类的标识符。 -以下各节解释应用程序如何请求特定的身份验证上下文类别,以及身份提供者如何提供声明以传递所使用的身份验证上下文类引用和/或身份验证机制。 +应用需要向身份提供者请求使用特定类别的身份验证机制来实现所需级别的身份验证保证。这可以通过身份验证上下文类引用来完成,身份验证上下文涉及多个因素,例如用于创建帐户的标识过程、防止凭据泄露的保护以及使用的身份验证机制。身份验证上下文类表示一组身份验证方法,身份验证上下文类引用是身份验证上下文类的标识符。 以下各节解释应用程序如何请求特定的身份验证上下文类别,以及身份提供者如何提供声明以传递所使用的身份验证上下文类引用和/或身份验证机制。 ### OIDC OIDC客户端可以使用认证请求的以下参数,按优先顺序请求一个或多个认证上下文类: -* acr_value–身份验证上下文类参考 +* acr\_value–身份验证上下文类参考 颁发给应用程序的ID令牌可以包含以下参数,以传递用于对ID令牌中引用的用户(主题)进行身份验证的身份验证上下文类和身份验证方法: @@ -57,7 +55,7 @@ OIDC客户端可以使用认证请求的以下参数,按优先顺序请求一 ### SAML2.0 -SAML2.0身份验证请求可以使用元素指定应用程序所需的身份验证上下文类。如果身份提供程序提供此信息,SAML2.0身份验证响应将在身份验证断言的元素中显示用于对用户进行身份验证的身份验证上下文类。应用程序(服务提供商)和身份提供商必须提前为不同的身份验证上下文类建立定义。文档“OASIS安全断言标记语言(SAML)V2.0的身份验证上下文”[vi](https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf)列出了几个可以使用的预定义身份验证上下文类。 +SAML2.0身份验证请求可以使用元素指定应用程序所需的身份验证上下文类。如果身份提供程序提供此信息,SAML2.0身份验证响应将在身份验证断言的元素中显示用于对用户进行身份验证的身份验证上下文类。应用程序(服务提供商)和身份提供商必须提前为不同的身份验证上下文类建立定义。文档“OASIS安全断言标记语言(SAML)V2.0的身份验证上下文”[vi](https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf)列出了几个可以使用的预定义身份验证上下文类。 ## 认证降级 @@ -69,22 +67,21 @@ SAML2.0身份验证请求可以使用元素指定应用 ## 本章重点回顾 -密码是一种相对较弱的身份验证形式,使用移动终端或多因素加密身份验证设备上生成的一次性密码被视为更强的形式。 -逐步身份验证是使用更强大的身份验证形式进行身份验证的行为,它将已经存在的身份验证会话提升到更高级别的身份验证保证。授权策略可能要求会话处于特定身份保证级别,以便访问敏感资源。 +密码是一种相对较弱的身份验证形式,使用移动终端或多因素加密身份验证设备上生成的一次性密码被视为更强的形式。 逐步身份验证是使用更强大的身份验证形式进行身份验证的行为,它将已经存在的身份验证会话提升到更高级别的身份验证保证。授权策略可能要求会话处于特定身份保证级别,以便访问敏感资源。 -OIDC和SAML 2.0都允许应用程序请求身份提供者使用特定的身份验证上下文类的身份验证机制对用户进行身份验证,并接收有关用于对用户进行身份验证的身份验证上下文类和/或身份验证方法的信息。 -通过缩短会话超时或注销来及时终止更高级别的会话是身份验证降级的推荐实现方式。 +OIDC和SAML 2.0都允许应用程序请求身份提供者使用特定的身份验证上下文类的身份验证机制对用户进行身份验证,并接收有关用于对用户进行身份验证的身份验证上下文类和/或身份验证方法的信息。 通过缩短会话超时或注销来及时终止更高级别的会话是身份验证降级的推荐实现方式。 ## 参考 -i. https://pages.nist.gov/800-63-3/sp800-63b.html +i. [https://pages.nist.gov/800-63-3/sp800-63b.html](https://pages.nist.gov/800-63-3/sp800-63b.html) -ii. https://pages.nist.gov/800-63-3/sp800-63-3.html#sec6 +ii. [https://pages.nist.gov/800-63-3/sp800-63-3.html\#sec6](https://pages.nist.gov/800-63-3/sp800-63-3.html#sec6) -iii.https://pages.nist.gov/800-63-3/sp800-63b.html +iii.[https://pages.nist.gov/800-63-3/sp800-63b.html](https://pages.nist.gov/800-63-3/sp800-63b.html) -iv.https://openid.net/specs/openid-connect-eap-acr-values-1_0.html +iv.[https://openid.net/specs/openid-connect-eap-acr-values-1\_0.html](https://openid.net/specs/openid-connect-eap-acr-values-1_0.html) -v.https://datatracker.ietf.org/doc/html/draft-ietf-oauth-amr-values-04 +v.[https://datatracker.ietf.org/doc/html/draft-ietf-oauth-amr-values-04](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-amr-values-04) + +vi.[https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf](https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf) -vi.https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf \ No newline at end of file diff --git a/docs/modernidentity/ChapterTwo.md b/docs/modernidentity/chaptertwo.md similarity index 99% rename from docs/modernidentity/ChapterTwo.md rename to docs/modernidentity/chaptertwo.md index 3a8dcea..71e7593 100644 --- a/docs/modernidentity/ChapterTwo.md +++ b/docs/modernidentity/chaptertwo.md @@ -28,8 +28,6 @@ 身份管理(IAM)系统是一组服务,支持创建、修改和删除身份和与其关联的帐户,以及访问资源所需的身份验证和授权。身份管理系统用于保护在线资源免受未经授权的访问,是应用安全模型的重要组成部分。 - - ## 身份生命周期里的事件 有了基本的定义,我们就可以继续讨论身份生命周期中的主要事件,如图 2-1所示. 本章我们将概述下在身份的生命周期当中会发生的事件,然后在后续章节中更深入地探讨每一个事件。事实上,基于事件去管理身份也是现代化的身份管理系统的主要思想。 @@ -42,11 +40,9 @@ 我们谈到的注册,通常是指用户,通过一个表单形式的注册页面,填写身份的属性信息,来生成一个账户。身份的供给实际上包含了一个更广泛的创建账户的范畴,身份供给可以通过让用户注册、从已有的系统中导入标识信息或利用已有系统的外部标识服务来完成,管理人员通过手工录入的方式来创建。无论使用何种机制,身份供给的目标都是建立一个具有关联身份属性数据的帐户。这涉及到获取或为用户实体分配身份的唯一标识符(如果需要进一步的分离,可以选择性的创建帐户的唯一标识符,这一标识符不同于身份的唯一标识符)、根据账户标识符创建帐户以及将身份相关的属性与该帐户相关联。 -例如,一个名为Alice的用户希望使用一些网上银行服务。爱丽丝可以通过填写账户登记表在银行建立一个个人网上银行账户。银行在为Alice创建账户时,会要求她提供身份相关的信息,包括姓名、密码、身份证号、家庭住址、(经过验证的)电话号码、电子邮件地址。 - -Alice在这个银行可以为不同的业务创建多个在线账户,如理财的账户、股票资金托管的账户等等。除此之外,Alice如果是个小企业主,Alice还可以使用自己公司的信息,如企业营业执照号码、税号、公司地址等信息,结合个人的身份信息,在银行创建自己企业的企业账户。 - +例如,一个名为Alice的用户希望使用一些网上银行服务。爱丽丝可以通过填写账户登记表在银行建立一个个人网上银行账户。银行在为Alice创建账户时,会要求她提供身份相关的信息,包括姓名、密码、身份证号、家庭住址、(经过验证的)电话号码、电子邮件地址。 +Alice在这个银行可以为不同的业务创建多个在线账户,如理财的账户、股票资金托管的账户等等。除此之外,Alice如果是个小企业主,Alice还可以使用自己公司的信息,如企业营业执照号码、税号、公司地址等信息,结合个人的身份信息,在银行创建自己企业的企业账户。 ## 身份映射(Mapping) @@ -66,8 +62,6 @@ Alice在银行建立了在线身份和账户后,就可以访问银行的网银 Alice可能首先使用用户名和密码登录,并能够在银行网站上查看她的帐户余额。如果Alice试图从账户中转出资金,则要求输入更强大的身份验证因素,才能成功执行该操作,比如,通过输入在银行登记过的手机收到的一次性动态码,或者进行动态的人脸识别,以确保请求转账业务的用户是本次操作帐户的所有者。 - - ## 身份认证状态(State) 身份认证状态(State),通常来讲,一个身份系统,基础的应该涉及到集中基础的状态,包括用户不存在、用户名或密码错误、认证成功等三种状态。但是如果考虑更安全的因素,认证的状态则要复杂的多,最基础的是用户的么密码是否过期,密码是否符合复杂度的要求,如果使用了一个简单的或者临时密码,则要求用户设置一个新的符合复杂性要求的密码。如果使用了MFA的多因素认证,则会涉及到更多的状态,如MFA是否登记,MFA在当前的认证流程中是否必须,MFA的挑战是否通过。只有通过了所有的安全的因素状态,系统才能给予认证成功,创建该用户的登录成功的会话。 @@ -82,8 +76,6 @@ Alice可能首先使用用户名和密码登录,并能够在银行网站上查 Alice的零售银行应用程序提供对其银行账户的访问,可能只允许相对较短的会话(以分钟为单位)。银行提供的另一种不太敏感的服务,如投资时事通讯,可能允许更长的会话时间,以小时或天为单位。每次Alice对任何一个应用程序发出请求时,应用程序都需要检查她最近是否对请求的事务进行了足够的身份验证。如果是这样,她可以继续而不需要再次验证。如果距离上次身份验证的时间超出了会话限制,她则将不得不再次进行身份验证。 - - ## 单点登录(SSO) 只需要通过一次认证,就可以访问所有授权的应用系统。单点登录(Single sign-on,SSO)是现代IT必备的一项基础功能,让用户和IT管理人员更加轻松应对工作需求。 @@ -94,8 +86,6 @@ Alice的零售银行应用程序提供对其银行账户的访问,可能只允 当Alice访问她的银行网站时,单点登录就可以让其方便地访问多个银行服务。比如,Alice在登录了网上银行的服务去查询账户余额之后,她在进行其他的一些被授权业务的访问,如理财资讯、积分管理等应用/服务时,她无需再次登录即可访问。 - - ## 注销(logout) 注销机制,是当用户在应用中完成需要处理的工作以后,应用程序提供的让用户终止当前会话一个方式。注销操作应该终止用户的应用程序会话,如果它们返回应用程序,则必须在被授予访问权限之前再次进行身份验证。如果实施了单点登录,那么可能有多个会话要终止,当用户从一个应用程序注销时,应该终止哪些会话是一个取决于整个系统的设计。 @@ -110,16 +100,12 @@ Alice的零售银行应用程序提供对其银行账户的访问,可能只允 例如,当Alice创建银行账户时,银行授权她的账户访问应用程序以查看存款账户,而且仅限于查看自己的存款账户而不能查看其他人的账户存款。如果她在银行没有股票资金托管账户,她的账户将无权进行股票交易资金的操作。Alice的授权表明她的帐户已被授予权限。对帐户的授权通常在创建帐户后完成,并且可能会随着时间的推移而更新。 - - ## 访问策略执行(Policy Enforcement) 一旦用户通过身份验证并与该帐户相关联,就必须严格的执行访问策略,以确保用户能够操作且仅限于之前被授予的权限。我们使用术语“访问策略执行”来匹配执行授权指定的访问策略。换句话说,授权指定允许用户或实体执行的操作,访问策略执行检查用户请求的操作是否符合授权使用的权限的约定。 当Alice登录到银行的网上银行应用程序并提出请求时,应用程序将检查她是否有权执行其提出请求。如果她试图访问非授权的业务,如没有开通理财账户,购买理财产品的请求将被拒绝,因为她无权访问这些服务。在这种情况下,应用程序可能会显示一条消息,指示不允许她查看该服务,其中可能包含有关如何申请开通该服务的信息。 - - ## 账户管理和恢复(Account Management) 在身份的整个生命周期中,可能需要更改用户身份资料的各种属性。例如,用户可能需要更新其电子邮件地址或电话号码。在某些情况下,用户可能需要更新其姓名,或者定期更改其密码或认证过程中使用的移动设备。在公司中,可能会更新用户身份资料以反映新的职位、地址或权限(如角色)。帐户管理由允许用户和管理员查看和更新与身份关联的用户资料的属性的功能或流程组成。 @@ -132,8 +118,6 @@ Alice的零售银行应用程序提供对其银行账户的访问,可能只允 可能有一天需要取消账户。在这种情况下,必须取消设置用户的帐户和关联的身份信息,以确保没有人再可以使用它。取消设置的形式可以是完全删除帐户和相关的身份信息,或者禁用帐户以保留信息用于审计目的。如果Alice在某个时候决定终止与银行的关系,她会要求关闭她的账户。银行将关闭她的储蓄账户,终止她的网上银行账户,这样她就不能再登录了。然而,为了确保该账户的所有行为和操作都是合规的,银行需要保留足够的历史信息,以满足后续的审计需求。 - - ## 本章关键点回顾 本章介绍了身份、帐户和相关标识的概念,以及它们存在期间发生的最典型事件,从认证、授权、会话和账户管理四个维度,分别进行了分类和每个类别下涉及到哪些事件,进行了详细的介绍。在接下来的章节中,我们将从身份管理的历史开始,逐步深入了解每个事件的更多细节。 diff --git a/docs/modernidentity/Introduction.md b/xian-dai-shen-fen-jian-she-zhi-nan/introduction.md similarity index 95% rename from docs/modernidentity/Introduction.md rename to xian-dai-shen-fen-jian-she-zhi-nan/introduction.md index 0dc7ebd..402932b 100644 --- a/docs/modernidentity/Introduction.md +++ b/xian-dai-shen-fen-jian-she-zhi-nan/introduction.md @@ -1,7 +1,5 @@ # 引言 - - 云计算、移动互联网、IoT、5G 等一批新技术不断采用,线上的业务与日俱增。另一方面,网络安全的形式也愈加紧迫。 网络安全工作人员缺口巨大且不断扩大,根据网络安全劳动力研究报告,全球网络安全人才缺口已扩大至近 300 万。其中,亚太地区缺口最高,达到了214 万。在需要安全保护的在线服务和设备数量激增的今天,网络安全面临的严峻风险令人震惊。为了填补这一空白,除了通过高校和社会机构培养更多的人才以外。必须鼓励更多的人了解这一领域,包括非安全专业的技术人员,例如开发者,鼓励他们更多的理解安全,并为他们提供足够的学习资源,使他们能够在开发阶段有效地选择安全的架构和安全的开发模式。 @@ -22,7 +20,7 @@ 期望通过本书的对于身份的概述,能够帮助你开始理解身份安全,为你提供足够的背景知识,以为你更全面地了解身份安全做好铺垫。 -对于身份安全,涵盖的内容如此之广,也期望读者对我们遗漏的内容、理解偏差的内容以提供更正和反馈。我们将该书发布在docs.oneauth.com,同时可通过https://github.com/OneAuth2/Oneauth 访问,后续发现的任何错误欢迎大家反馈,对于一些错误的更正,也都将在该书的OneAuth GitHub repo中注明。 +对于身份安全,涵盖的内容如此之广,也期望读者对我们遗漏的内容、理解偏差的内容以提供更正和反馈。我们将该书发布在docs.oneauth.com,同时可通过[https://github.com/OneAuth2/Oneauth](https://github.com/OneAuth2/Oneauth) 访问,后续发现的任何错误欢迎大家反馈,对于一些错误的更正,也都将在该书的OneAuth GitHub repo中注明。 我们希望这本书和示例代码对您有用,并祝您的应用程序项目好运和安全。 From 2b0106a558d2613e3d118b6fba718f0256aaaa2d Mon Sep 17 00:00:00 2001 From: David Date: Sun, 26 Sep 2021 09:22:26 +0000 Subject: [PATCH 07/15] GitBook: [books] one page modified --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ab1ed96..330e4a7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ +--- +description: An awesome identity book talk about how to build a morden identity. +--- + # 现代身份建设指南 ## 一本关于如何做好身份管理的书 -## An awesome identity book talk about how to build a morden identity. +## From 733ed734d7131dfe2a0efc91d4ec6abda7bc80a1 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 26 Sep 2021 09:22:44 +0000 Subject: [PATCH 08/15] GitBook: [books] one page modified --- SUMMARY.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index a969131..c939f66 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -1,6 +1,13 @@ # Table of contents * [现代身份建设指南](README.md) + +## 现代身份建设指南 + +* [引言](xian-dai-shen-fen-jian-she-zhi-nan/introduction.md) + +--- + * [docs](docs/README.md) * [modernidentity](docs/modernidentity/README.md) * [第五章 API安全的成熟度模型](docs/modernidentity/chapterfive.md) @@ -16,7 +23,3 @@ * [第十章 Sessions 会话](docs/modernidentity/chapterten.md) * [第十二章 强身份认证](docs/modernidentity/chaptertwelve.md) -## 现代身份建设指南 - -* [引言](xian-dai-shen-fen-jian-she-zhi-nan/introduction.md) - From 955c65071e2abd5412eb53606a92062502bf0ace Mon Sep 17 00:00:00 2001 From: David Date: Sun, 26 Sep 2021 09:26:36 +0000 Subject: [PATCH 09/15] GitBook: [books] one page modified --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 330e4a7..315e2dc 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ description: An awesome identity book talk about how to build a morden identity. # 现代身份建设指南 -## 一本关于如何做好身份管理的书 +## 一本如何做好现代身份管理的书,身份建设的要点给出详细的指导,现代身份建设实践指南 ## From f8586f3726ac14e72e913be09b31f66897bfea32 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 26 Sep 2021 09:27:38 +0000 Subject: [PATCH 10/15] GitBook: [books] one page modified --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 315e2dc..b20a864 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ description: An awesome identity book talk about how to build a morden identity. # 现代身份建设指南 -## 一本如何做好现代身份管理的书,身份建设的要点给出详细的指导,现代身份建设实践指南 +## 一本如何做好现代身份管理的书 ## From 401a0309275b148b74f49524800aab3f39c931f0 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 26 Sep 2021 09:30:22 +0000 Subject: [PATCH 11/15] GitBook: [books] 27 pages modified --- SUMMARY.md | 29 ++++++++----------- docs/README.md | 2 -- docs/modernidentity/README.md | 2 -- .../chaptereight.md | 0 .../chaptereleven.md | 0 .../chapterfive.md | 0 .../chapterfour.md | 0 .../chapternine.md | 0 .../chapterone.md | 0 .../chapterseven.md | 0 .../chaptersix.md | 0 .../chapterten.md | 0 .../chapterthree.md | 0 .../chaptertwelve.md | 0 .../chaptertwo.md | 0 15 files changed, 12 insertions(+), 21 deletions(-) delete mode 100644 docs/README.md delete mode 100644 docs/modernidentity/README.md rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chaptereight.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chaptereleven.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chapterfive.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chapterfour.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chapternine.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chapterone.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chapterseven.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chaptersix.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chapterten.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chapterthree.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chaptertwelve.md (100%) rename {docs/modernidentity => xian-dai-shen-fen-jian-she-zhi-nan}/chaptertwo.md (100%) diff --git a/SUMMARY.md b/SUMMARY.md index c939f66..73ead20 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -5,21 +5,16 @@ ## 现代身份建设指南 * [引言](xian-dai-shen-fen-jian-she-zhi-nan/introduction.md) - ---- - -* [docs](docs/README.md) - * [modernidentity](docs/modernidentity/README.md) - * [第五章 API安全的成熟度模型](docs/modernidentity/chapterfive.md) - * [第九章 授权与访问策略执行](docs/modernidentity/chapternine.md) - * [第八章 深入学习SAML2.0](docs/modernidentity/chaptereight.md) - * [第七章 OpenID Connect](docs/modernidentity/chapterseven.md) - * [第二章 身份的生命周期](docs/modernidentity/chaptertwo.md) - * [第一章 现代身份的挑战](docs/modernidentity/chapterone.md) - * [第四章 身份的供给/生成](docs/modernidentity/chapterfour.md) - * [第六章 深入学习OAuth2.0](docs/modernidentity/chaptersix.md) - * [第十一章 单点登录SSO](docs/modernidentity/chaptereleven.md) - * [第三章 身份演进历史](docs/modernidentity/chapterthree.md) - * [第十章 Sessions 会话](docs/modernidentity/chapterten.md) - * [第十二章 强身份认证](docs/modernidentity/chaptertwelve.md) +* [第一章 现代身份的挑战](xian-dai-shen-fen-jian-she-zhi-nan/chapterone.md) +* [第二章 身份的生命周期](xian-dai-shen-fen-jian-she-zhi-nan/chaptertwo.md) +* [第三章 身份演进历史](xian-dai-shen-fen-jian-she-zhi-nan/chapterthree.md) +* [第四章 身份的供给/生成](xian-dai-shen-fen-jian-she-zhi-nan/chapterfour.md) +* [第五章 API安全的成熟度模型](xian-dai-shen-fen-jian-she-zhi-nan/chapterfive.md) +* [第六章 深入学习OAuth2.0](xian-dai-shen-fen-jian-she-zhi-nan/chaptersix.md) +* [第七章 OpenID Connect](xian-dai-shen-fen-jian-she-zhi-nan/chapterseven.md) +* [第八章 深入学习SAML2.0](xian-dai-shen-fen-jian-she-zhi-nan/chaptereight.md) +* [第九章 授权与访问策略执行](xian-dai-shen-fen-jian-she-zhi-nan/chapternine.md) +* [第十章 Sessions 会话](xian-dai-shen-fen-jian-she-zhi-nan/chapterten.md) +* [第十一章 单点登录SSO](xian-dai-shen-fen-jian-she-zhi-nan/chaptereleven.md) +* [第十二章 强身份认证](xian-dai-shen-fen-jian-she-zhi-nan/chaptertwelve.md) diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 7dd802a..0000000 --- a/docs/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# docs - diff --git a/docs/modernidentity/README.md b/docs/modernidentity/README.md deleted file mode 100644 index 30d96ea..0000000 --- a/docs/modernidentity/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# modernidentity - diff --git a/docs/modernidentity/chaptereight.md b/xian-dai-shen-fen-jian-she-zhi-nan/chaptereight.md similarity index 100% rename from docs/modernidentity/chaptereight.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chaptereight.md diff --git a/docs/modernidentity/chaptereleven.md b/xian-dai-shen-fen-jian-she-zhi-nan/chaptereleven.md similarity index 100% rename from docs/modernidentity/chaptereleven.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chaptereleven.md diff --git a/docs/modernidentity/chapterfive.md b/xian-dai-shen-fen-jian-she-zhi-nan/chapterfive.md similarity index 100% rename from docs/modernidentity/chapterfive.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chapterfive.md diff --git a/docs/modernidentity/chapterfour.md b/xian-dai-shen-fen-jian-she-zhi-nan/chapterfour.md similarity index 100% rename from docs/modernidentity/chapterfour.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chapterfour.md diff --git a/docs/modernidentity/chapternine.md b/xian-dai-shen-fen-jian-she-zhi-nan/chapternine.md similarity index 100% rename from docs/modernidentity/chapternine.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chapternine.md diff --git a/docs/modernidentity/chapterone.md b/xian-dai-shen-fen-jian-she-zhi-nan/chapterone.md similarity index 100% rename from docs/modernidentity/chapterone.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chapterone.md diff --git a/docs/modernidentity/chapterseven.md b/xian-dai-shen-fen-jian-she-zhi-nan/chapterseven.md similarity index 100% rename from docs/modernidentity/chapterseven.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chapterseven.md diff --git a/docs/modernidentity/chaptersix.md b/xian-dai-shen-fen-jian-she-zhi-nan/chaptersix.md similarity index 100% rename from docs/modernidentity/chaptersix.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chaptersix.md diff --git a/docs/modernidentity/chapterten.md b/xian-dai-shen-fen-jian-she-zhi-nan/chapterten.md similarity index 100% rename from docs/modernidentity/chapterten.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chapterten.md diff --git a/docs/modernidentity/chapterthree.md b/xian-dai-shen-fen-jian-she-zhi-nan/chapterthree.md similarity index 100% rename from docs/modernidentity/chapterthree.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chapterthree.md diff --git a/docs/modernidentity/chaptertwelve.md b/xian-dai-shen-fen-jian-she-zhi-nan/chaptertwelve.md similarity index 100% rename from docs/modernidentity/chaptertwelve.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chaptertwelve.md diff --git a/docs/modernidentity/chaptertwo.md b/xian-dai-shen-fen-jian-she-zhi-nan/chaptertwo.md similarity index 100% rename from docs/modernidentity/chaptertwo.md rename to xian-dai-shen-fen-jian-she-zhi-nan/chaptertwo.md From 7dda0eb79446475504248e5ce06603d2d6dd11e9 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 26 Sep 2021 09:31:54 +0000 Subject: [PATCH 12/15] GitBook: [books] 2 pages modified --- SUMMARY.md | 4 ++++ guan-yu-shen-fen/untitled.md | 2 ++ 2 files changed, 6 insertions(+) create mode 100644 guan-yu-shen-fen/untitled.md diff --git a/SUMMARY.md b/SUMMARY.md index 73ead20..20c8aff 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -18,3 +18,7 @@ * [第十一章 单点登录SSO](xian-dai-shen-fen-jian-she-zhi-nan/chaptereleven.md) * [第十二章 强身份认证](xian-dai-shen-fen-jian-she-zhi-nan/chaptertwelve.md) +## 关于身份 + +* [Untitled](guan-yu-shen-fen/untitled.md) + diff --git a/guan-yu-shen-fen/untitled.md b/guan-yu-shen-fen/untitled.md new file mode 100644 index 0000000..5094080 --- /dev/null +++ b/guan-yu-shen-fen/untitled.md @@ -0,0 +1,2 @@ +# Untitled + From 3f84953d250eecae87ddb65ee998279dba006372 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 26 Sep 2021 09:32:11 +0000 Subject: [PATCH 13/15] GitBook: [books] 2 pages modified --- SUMMARY.md | 2 -- guan-yu-shen-fen/untitled.md | 2 -- 2 files changed, 4 deletions(-) delete mode 100644 guan-yu-shen-fen/untitled.md diff --git a/SUMMARY.md b/SUMMARY.md index 20c8aff..5c8fbaa 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -20,5 +20,3 @@ ## 关于身份 -* [Untitled](guan-yu-shen-fen/untitled.md) - diff --git a/guan-yu-shen-fen/untitled.md b/guan-yu-shen-fen/untitled.md deleted file mode 100644 index 5094080..0000000 --- a/guan-yu-shen-fen/untitled.md +++ /dev/null @@ -1,2 +0,0 @@ -# Untitled - From 55b15a6431271837dd5ef27255dfcc623b25f58f Mon Sep 17 00:00:00 2001 From: David Date: Sun, 26 Sep 2021 09:32:31 +0000 Subject: [PATCH 14/15] GitBook: [books] 3 pages modified --- SUMMARY.md | 5 ++++- .../chapterone.md => chapterone.md | 0 2 files changed, 4 insertions(+), 1 deletion(-) rename xian-dai-shen-fen-jian-she-zhi-nan/chapterone.md => chapterone.md (100%) diff --git a/SUMMARY.md b/SUMMARY.md index 5c8fbaa..b23c3e6 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -5,7 +5,6 @@ ## 现代身份建设指南 * [引言](xian-dai-shen-fen-jian-she-zhi-nan/introduction.md) -* [第一章 现代身份的挑战](xian-dai-shen-fen-jian-she-zhi-nan/chapterone.md) * [第二章 身份的生命周期](xian-dai-shen-fen-jian-she-zhi-nan/chaptertwo.md) * [第三章 身份演进历史](xian-dai-shen-fen-jian-she-zhi-nan/chapterthree.md) * [第四章 身份的供给/生成](xian-dai-shen-fen-jian-she-zhi-nan/chapterfour.md) @@ -20,3 +19,7 @@ ## 关于身份 +--- + +* [第一章 现代身份的挑战](chapterone.md) + diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chapterone.md b/chapterone.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chapterone.md rename to chapterone.md From 4295770624bb0adef48dbcc954d9818ee9b3e90f Mon Sep 17 00:00:00 2001 From: David Date: Sun, 26 Sep 2021 09:34:50 +0000 Subject: [PATCH 15/15] GitBook: [books] 23 pages modified --- SUMMARY.md | 28 +++++++++++-------- .../chapterfive.md => chapterfive.md | 0 .../chapterfour.md => chapterfour.md | 0 .../chapterthree.md => chapterthree.md | 0 .../chaptertwo.md => chaptertwo.md | 0 .../chaptereleven.md | 0 .../chapternine.md | 0 .../chapterten.md | 0 .../chaptertwelve.md | 0 .../chaptereight.md | 0 .../chapterseven.md | 0 .../chaptersix.md | 0 12 files changed, 17 insertions(+), 11 deletions(-) rename xian-dai-shen-fen-jian-she-zhi-nan/chapterfive.md => chapterfive.md (100%) rename xian-dai-shen-fen-jian-she-zhi-nan/chapterfour.md => chapterfour.md (100%) rename xian-dai-shen-fen-jian-she-zhi-nan/chapterthree.md => chapterthree.md (100%) rename xian-dai-shen-fen-jian-she-zhi-nan/chaptertwo.md => chaptertwo.md (100%) rename {xian-dai-shen-fen-jian-she-zhi-nan => she-ji}/chaptereleven.md (100%) rename {xian-dai-shen-fen-jian-she-zhi-nan => she-ji}/chapternine.md (100%) rename {xian-dai-shen-fen-jian-she-zhi-nan => she-ji}/chapterten.md (100%) rename {xian-dai-shen-fen-jian-she-zhi-nan => she-ji}/chaptertwelve.md (100%) rename {xian-dai-shen-fen-jian-she-zhi-nan => xie-yi}/chaptereight.md (100%) rename {xian-dai-shen-fen-jian-she-zhi-nan => xie-yi}/chapterseven.md (100%) rename {xian-dai-shen-fen-jian-she-zhi-nan => xie-yi}/chaptersix.md (100%) diff --git a/SUMMARY.md b/SUMMARY.md index b23c3e6..aa55dea 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -5,21 +5,27 @@ ## 现代身份建设指南 * [引言](xian-dai-shen-fen-jian-she-zhi-nan/introduction.md) -* [第二章 身份的生命周期](xian-dai-shen-fen-jian-she-zhi-nan/chaptertwo.md) -* [第三章 身份演进历史](xian-dai-shen-fen-jian-she-zhi-nan/chapterthree.md) -* [第四章 身份的供给/生成](xian-dai-shen-fen-jian-she-zhi-nan/chapterfour.md) -* [第五章 API安全的成熟度模型](xian-dai-shen-fen-jian-she-zhi-nan/chapterfive.md) -* [第六章 深入学习OAuth2.0](xian-dai-shen-fen-jian-she-zhi-nan/chaptersix.md) -* [第七章 OpenID Connect](xian-dai-shen-fen-jian-she-zhi-nan/chapterseven.md) -* [第八章 深入学习SAML2.0](xian-dai-shen-fen-jian-she-zhi-nan/chaptereight.md) -* [第九章 授权与访问策略执行](xian-dai-shen-fen-jian-she-zhi-nan/chapternine.md) -* [第十章 Sessions 会话](xian-dai-shen-fen-jian-she-zhi-nan/chapterten.md) -* [第十一章 单点登录SSO](xian-dai-shen-fen-jian-she-zhi-nan/chaptereleven.md) -* [第十二章 强身份认证](xian-dai-shen-fen-jian-she-zhi-nan/chaptertwelve.md) ## 关于身份 --- * [第一章 现代身份的挑战](chapterone.md) +* [第二章 身份的生命周期](chaptertwo.md) +* [第三章 身份演进历史](chapterthree.md) +* [第四章 身份的供给/生成](chapterfour.md) +* [第五章 API安全的成熟度模型](chapterfive.md) + +## 协议 + +* [第六章 深入学习OAuth2.0](xie-yi/chaptersix.md) +* [第七章 OpenID Connect](xie-yi/chapterseven.md) +* [第八章 深入学习SAML2.0](xie-yi/chaptereight.md) + +## 设计 + +* [第九章 授权与访问策略执行](she-ji/chapternine.md) +* [第十章 Sessions 会话](she-ji/chapterten.md) +* [第十一章 单点登录SSO](she-ji/chaptereleven.md) +* [第十二章 强身份认证](she-ji/chaptertwelve.md) diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chapterfive.md b/chapterfive.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chapterfive.md rename to chapterfive.md diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chapterfour.md b/chapterfour.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chapterfour.md rename to chapterfour.md diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chapterthree.md b/chapterthree.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chapterthree.md rename to chapterthree.md diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chaptertwo.md b/chaptertwo.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chaptertwo.md rename to chaptertwo.md diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chaptereleven.md b/she-ji/chaptereleven.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chaptereleven.md rename to she-ji/chaptereleven.md diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chapternine.md b/she-ji/chapternine.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chapternine.md rename to she-ji/chapternine.md diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chapterten.md b/she-ji/chapterten.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chapterten.md rename to she-ji/chapterten.md diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chaptertwelve.md b/she-ji/chaptertwelve.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chaptertwelve.md rename to she-ji/chaptertwelve.md diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chaptereight.md b/xie-yi/chaptereight.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chaptereight.md rename to xie-yi/chaptereight.md diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chapterseven.md b/xie-yi/chapterseven.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chapterseven.md rename to xie-yi/chapterseven.md diff --git a/xian-dai-shen-fen-jian-she-zhi-nan/chaptersix.md b/xie-yi/chaptersix.md similarity index 100% rename from xian-dai-shen-fen-jian-she-zhi-nan/chaptersix.md rename to xie-yi/chaptersix.md