安全斷言標記語言(英語:Security Assertion Markup Language,簡稱SAML,發音 sam-el)是一個基于 XML 的開源標準數據格式,它在當事方之間交換身份驗證和授權數據,尤其是在身份提供者和服務提供者之間交換。SAML2.0 可以實現基于網絡跨域的單點登錄(SSO), 以便于減少向一個用戶分發多個身份驗證令牌的管理開銷。
在 SAML 協議中,涉及兩個主體:
Service Provider 服務提供方,簡稱 SP。什么是服務提供方?例如:阿里云控制臺、騰訊云控制臺、AWS 控制臺這些都是服務提供方。
Identity Provider 身份提供方,簡稱 IdP。什么是身份提供方?Authing 可以作為身份提供方,身份提供方能夠向 SP 發送身份斷言,所謂身份斷言就是由 Authing 簽發的,可以標識某個人身份的 Token,只不過,在 SAML 協議中,這個 Token 的格式是 XML 形式的。還有一些其他的身份提供方,例如 Okta、SSOCircle、Auth0,他們都可以向 SP 返回身份斷言。
兩個主體通過用戶的瀏覽器進行信息交換。方式上,SP 可以返回帶參數的重定向 HTTP 響應,讓用戶立刻通過參數將信息發給 IdP。而 IdP 會返回一個表單,同時還有一段立即提交表單的 JS 代碼,從而讓用戶立刻將信息發給 SP。
總結一下,SP 提供服務,需要知道用戶的身份,就需要向 IdP 詢問。IdP 知道用戶的身份,當用戶在 IdP 登錄成功,IdP 就將用戶的身份以 SAML 斷言的形式發給 SP。SP 信任 IdP 發來的身份斷言,從而賦予該用戶在 SP 的相關權限。
當用戶的身份無法鑒定時,SP 會向 IdP 發送 SAML Request 信息(通過瀏覽器發送),請求 IdP 來鑒定用戶身份。
由阿里云控制臺發起一次 SAML Request 的形式是這樣的:
GET https://core.authing.cn/v2/api/saml-idp/5e10927e4ecfd464fb4edaf6?SAMLRequest=fZJLT%2BMwFIX3%2FIrI%2B7yct9Wk6kyFQGJERQKL2RnnJnWV2Blfp2L%2BPaGlDLOApaV7vnN0jlfrl3FwjmBQalWS0AuIA0roVqq%2BJI%2FNtZuTdXW1Qj4OdGKb2e7VA%2FyZAa2zQQRjF91PrXAewdRgjlLA48NdSfbWTsh8H2WvpPL4IP%2FOyhN69N9Qfl3fE2e7UKTi9mR9EQhtwOOLz5LAE8o%2FUp9P8qRyZTv5CYRBQTOIQXRtnMbdcwwt71LiXGsj4JSwJB0fEIhzuy0Jp9AXgvaHgwzzPA%2FjfXagbRYlebeP%2BmI5wh1HlEf4J0Oc4Vah5cqWhAY0cIPCpXkTRiwoWJJ5eZH%2BJs7OaKuFHn5IdS5sNoppjhKZ4iMgs4LVm193jHoBez4fIbtpmp27u68b4jxdiqdvxS9TKGTnqr9nTe%2FGpDovw06JzWfC9wB%2B2Y5UXy8VRlmcpkWUpUlGY5p8TLfyP7tW78%2F%2Fv0f1Cg%3D%3D
(提示:代碼可向右滑動)
SAMLRequest 參數通過 query 在 URL 中發送給 IdP,SAMLRequest 的內容如下:
fZJLT+MwFIX3/IrI+7yct9Wk6kyFQGJERQKL2RnnJnWV2Blfp2L+PaGlDLOApaV7vnN0jlfrl3FwjmBQalWS0AuIA0roVqq+JI/NtZuTdXW1Qj4OdGKb2e7VA/yZAa2zQQRjF91PrXAewdRgjlLA48NdSfbWTsh8H2WvpPL4IP/OyhN69N9Qfl3fE2e7UKTi9mR9EQhtwOOLz5LAE8o/Up9P8qRyZTv5CYRBQTOIQXRtnMbdcwwt71LiXGsj4JSwJB0fEIhzuy0Jp9AXgvaHgwzzPA/jfXagbRYlebeP+mI5wh1HlEf4J0Oc4Vah5cqWhAY0cIPCpXkTRiwoWJJ5eZH+Js7OaKuFHn5IdS5sNoppjhKZ4iMgs4LVm193jHoBez4fIbtpmp27u68b4jxdiqdvxS9TKGTnqr9nTe/GpDovw06JzWfC9wB+2Y5UXy8VRlmcpkWUpUlGY5p8TLfyP7tW78//v0f1Cg==
(提示:代碼可向右滑動)
base64 decode + inflate 解碼后
(https://www.samltool.com/decode.php)
<?xml version="1.0" encoding="UTF-8"?><saml2p:AuthnRequest AssertionConsumerServiceURL="https://signin.aliyun.com/saml/SSO" Destination="https://core.authing.cn/v2/api/saml-idp/5e10927e4ecfd464fb4edaf6" ForceAuthn="false" ID="a2eg9c2gjji188814h7j2d7358fh3g9" IsPassive="false" IssueInstant="2020-09-28T13:09:57.896Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"> <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://signin.aliyun.com/1374669376572425/saml/SSO </saml2:Issuer></saml2p:AuthnRequest>
(提示:代碼可向右滑動)
IdP 收到 SAML Request 后,會彈出登錄框對用戶身份進行認證:
當用戶在 IdP 完成登錄后,SAML IdP 將用戶身份斷言發送給 SP(放在表單中,通過瀏覽器 POST 請求發送)。SAML IdP 的響應內容如下:
<form id="saml-form" method="post" action="https://signin.aliyun.com/saml/SSO" autocomplete="off"> <input type="hidden" name="SAMLResponse" id="saml-response" value="PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfNjJiMTc3YzEtYTkxOS00MmY2LTk1ODYtNDdmMTNiNzEwODFmIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyMC0wOS0yOFQxMzozMDozMS43ODhaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9zaWduaW4uYWxpeXVuLmNvbS9zYW1sL1NTTyIgSW5SZXNwb25zZVRvPSJhNDlmOGVkaTMxY2owYTJhNDU5ZzAzMzFjM2Q5YzEwIj4KICA8c2FtbDpJc3N1ZXI+aHR0cHM6Ly8yMG5xdWx2b3FwYnAuYXV0aGluZy5jbjwvc2FtbDpJc3N1ZXI+CiAgPHNhbWxwOlN0YXR1cz4KICAgIDxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz4KICA8L3NhbWxwOlN0YXR1cz4KICA8c2FtbDpBc3NlcnRpb24geG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iX2ZhZTk1YjQ3LWNiZjMtNGEyMC1hZGQwLTk5ZDg1NmI0MTI0ZSIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMjAtMDktMjhUMTM6MzA6MzEuNzg4WiI+CiAgICA8c2FtbDpJc3N1ZXI+aHR0cHM6Ly8yMG5xdWx2b3FwYnAuYXV0aGluZy5jbjwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+PGRzOlJlZmVyZW5jZSBVUkk9IiNfZmFlOTViNDctY2JmMy00YTIwLWFkZDAtOTlkODU2YjQxMjRlIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT4vb2w2bEMxaitzbWRvbmw0OCtsSlR6VWVxbnc9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPmF3emNFMGRwOEJ6VFc0YjRQRmFSWDdOS09DOTViTHFPblBlQUtJL0NzRGZHYUpkbXpDSzBmVmxpeitlNlh6Qmx1S2ZCcFF0clFvbktsN2sydlZOYVBGeDlQcFNWendLOTFITEd2WVEwcUIzNnVBNEhGdm0vM00zMURMM1pSRlBScTY4WmFWQUc2bE1WZDBZYmlJblZ2OUZXd3NpKzZqRXBGK1BSbG1rb3FBST08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNRakNDQWF1Z0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRMEZBREErTVFzd0NRWURWUVFHRXdKMWN6RVNNQkFHQTFVRUNBd0o1THFMNWE2ZTVMaUtNUXd3Q2dZRFZRUUtEQU56YzNNeERUQUxCZ05WQkFNTUJITnpjM013SGhjTk1qQXdNVEF6TVRNeE9ERTBXaGNOTWpFd01UQXlNVE14T0RFMFdqQStNUXN3Q1FZRFZRUUdFd0oxY3pFU01CQUdBMVVFQ0F3SjVMcUw1YTZlNUxpS01Rd3dDZ1lEVlFRS0RBTnpjM014RFRBTEJnTlZCQU1NQkhOemMzTXdnWjh3RFFZSktvWklodmNOQVFFQkJRQURnWTBBTUlHSkFvR0JBTU5XbE1rNEwrVGNXd3lkOXBsVFBMaEhML1VNQ1BHSmd2NVZwOHZhQXA0V01zR3R3T0xJMVVOV2NjSXFNZVUwS2FzSnFyS3hIWXZxOUp6Wmg0ZmZ0Rm1vd0J6MzZ2ejBlSVVzUDVQS3ZGVUxrQzF2anJkbitRSlhiSjUxYWxaWktmUGdsMUhJOHc2bGgxMmFXVGphS1ErS2VtSXR0cUxxSmdMV09ZQVhQSXN6QWdNQkFBR2pVREJPTUIwR0ExVWREZ1FXQkJUVDEwNGhWWVZuUHBnN2FGckRpWFBTaGJ0eFVUQWZCZ05WSFNNRUdEQVdnQlRUMTA0aFZZVm5QcGc3YUZyRGlYUFNoYnR4VVRBTUJnTlZIUk1FQlRBREFRSC9NQTBHQ1NxR1NJYjNEUUVCRFFVQUE0R0JBQjYrMXhLN0dNSmE1TTZVamcvd2Q0RXR3eThOZFRGNnlwU3FOMzZCZDVPZFBtd1U5SHpEdUdqS2kzWndvb1BJR1JCOHBpTHNLazExTTRJaEFGNEMyUi9Kc3ZWWXdXT1lnb2pXNEgxaFI1d2syam43cGx0V3FSUGRmWkJsMFlmc0R5c1VQN2s4L01jaE9XWDdXaWZOeHBlM0dkU0tOMTdDa2RSakw5MjRiVjBsPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+CiAgICA8c2FtbDpTdWJqZWN0PgogICAgICA8c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDp1bnNwZWNpZmllZCI+eWV6dXdlaUBhdXRoaW5nLm9uYWxpeXVuLmNvbTwvc2FtbDpOYW1lSUQ+CiAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4KICAgICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMjAtMDktMjhUMTQ6MzA6MzEuNzg4WiIgUmVjaXBpZW50PSJodHRwczovL3NpZ25pbi5hbGl5dW4uY29tL3NhbWwvU1NPIiBJblJlc3BvbnNlVG89ImE0OWY4ZWRpMzFjajBhMmE0NTlnMDMzMWMzZDljMTAiLz4KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+CiAgICA8L3NhbWw6U3ViamVjdD4KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDIwLTA5LTI4VDEzOjMwOjMxLjc4OFoiIE5vdE9uT3JBZnRlcj0iMjAyMC0wOS0yOFQxNDozMDozMS43ODhaIj4KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwczovL3NpZ25pbi5hbGl5dW4uY29tLzEzNzQ2NjkzNzY1NzI0MjUvc2FtbC9TU088L3NhbWw6QXVkaWVuY2U+CiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgPC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDIwLTA5LTI4VDEzOjMwOjMxLjg4OFoiIFNlc3Npb25JbmRleD0ib29ldW1jcTZlSGpkZHIxSDNGeXpvdTdDcy1PR1RzTmwiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3Nlczp1bnNwZWNpZmllZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImVtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPnllenV3ZWlAYXV0aGluZy5jbjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJuYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciLz48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1c2VybmFtZSIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj55ZXp1d2VpQGF1dGhpbmcuY248L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0icGhvbmUiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+bnVsbDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj4KPC9zYW1scDpSZXNwb25zZT4=" /> <input type="hidden" name="RelayState" id="relay-state" value="" /></form><script type="text/javascript"> (function() { document.forms[0].submit(); })();</script>
(提示:代碼可向右滑動)
沒有什么神秘的,就是一個 HTML form 表單和一段立即提交該表單的 JS 代碼。其中的 SAML Response 信息如下:
base64 decode + inflate 解碼后
(https://www.samltool.com/decode.php)
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_62b177c1-a919-42f6-9586-47f13b71081f" Version="2.0" IssueInstant="2020-09-28T13:30:31.788Z" Destination="https://signin.aliyun.com/saml/SSO" InResponseTo="a49f8edi31cj0a2a459g0331c3d9c10"> <saml:Issuer>https://20nqulvoqpbp.authing.cn</saml:Issuer> <samlp:Status> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> </samlp:Status> <saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_fae95b47-cbf3-4a20-add0-99d856b4124e" Version="2.0" IssueInstant="2020-09-28T13:30:31.788Z"> <saml:Issuer>https://20nqulvoqpbp.authing.cn</saml:Issuer> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <ds:Reference URI="#_fae95b47-cbf3-4a20-add0-99d856b4124e"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>/ol6lC1j+smdonl48+lJTzUeqnw=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue>awzcE0dp8BzTW4b4PFaRX7NKOC95bLqOnPeAKI/CsDfGaJdmzCK0fVliz+e6XzBluKfBpQtrQonKl7k2vVNaPFx9PpSVzwK91HLGvYQ0qB36uA4HFvm/3M31DL3ZRFPRq68ZaVAG6lMVd0YbiInVv9FWwsi+6jEpF+PRlmkoqAI=</ds:SignatureValue> <ds:KeyInfo> <ds:X509Data> <ds:X509Certificate>MIICQjCCAaugAwIBAgIBADANBgkqhkiG9w0BAQ0FADA+MQswCQYDVQQGEwJ1czESMBAGA1UECAwJ5LqL5a6e5LiKMQwwCgYDVQQKDANzc3MxDTALBgNVBAMMBHNzc3MwHhcNMjAwMTAzMTMxODE0WhcNMjEwMTAyMTMxODE0WjA+MQswCQYDVQQGEwJ1czESMBAGA1UECAwJ5LqL5a6e5LiKMQwwCgYDVQQKDANzc3MxDTALBgNVBAMMBHNzc3MwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMNWlMk4L+TcWwyd9plTPLhHL/UMCPGJgv5Vp8vaAp4WMsGtwOLI1UNWccIqMeU0KasJqrKxHYvq9JzZh4fftFmowBz36vz0eIUsP5PKvFULkC1vjrdn+QJXbJ51alZZKfPgl1HI8w6lh12aWTjaKQ+KemIttqLqJgLWOYAXPIszAgMBAAGjUDBOMB0GA1UdDgQWBBTT104hVYVnPpg7aFrDiXPShbtxUTAfBgNVHSMEGDAWgBTT104hVYVnPpg7aFrDiXPShbtxUTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAB6+1xK7GMJa5M6Ujg/wd4Etwy8NdTF6ypSqN36Bd5OdPmwU9HzDuGjKi3ZwooPIGRB8piLsKk11M4IhAF4C2R/JsvVYwWOYgojW4H1hR5wk2jn7pltWqRPdfZBl0YfsDysUP7k8/MchOWX7WifNxpe3GdSKN17CkdRjL924bV0l</ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </ds:Signature> <saml:Subject> <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">yezuwei@authing.onaliyun.com</saml:NameID> <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <saml:SubjectConfirmationData NotOnOrAfter="2020-09-28T14:30:31.788Z" Recipient="https://signin.aliyun.com/saml/SSO" InResponseTo="a49f8edi31cj0a2a459g0331c3d9c10"/> </saml:SubjectConfirmation> </saml:Subject> <saml:Conditions NotBefore="2020-09-28T13:30:31.788Z" NotOnOrAfter="2020-09-28T14:30:31.788Z"> <saml:AudienceRestriction> <saml:Audience>https://signin.aliyun.com/1374669376572425/saml/SSO</saml:Audience> </saml:AudienceRestriction> </saml:Conditions> <saml:AuthnStatement AuthnInstant="2020-09-28T13:30:31.888Z" SessionIndex="ooeumcq6eHjddr1H3Fyzou7Cs-OGTsNl"> <saml:AuthnContext> <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef> </saml:AuthnContext> </saml:AuthnStatement> <saml:AttributeStatement> <saml:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">yezuwei@authing.cn </saml:AttributeValue> </saml:Attribute> <saml:Attribute Name="name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string"/> </saml:Attribute> <saml:Attribute Name="username" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">yezuwei@authing.cn </saml:AttributeValue> </saml:Attribute> <saml:Attribute Name="phone" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">null </saml:AttributeValue> </saml:Attribute> </saml:AttributeStatement> </saml:Assertion> </samlp:Response>
(提示:代碼可向右滑動)
這段內容就是用戶的身份斷言,也就是用戶的 Token,只不過這個 Token 通過 XML 格式傳遞。
讀到這里,你可能會對 SP、IdP 如何處理這些冗長的 XML 信息感到困惑。Authing 會解決這些繁瑣的處理,而你只需關注如何正確地配置 Authing IdP,與 SAML SP 進行通信。
本文為讀者講述 SAML 中,SP、IdP、瀏覽器三個實體之間數據交互的流程。
使用 SAML 協議進行身份認證時,涉及到以下三個主體
SP 與 IdP 之間的通信方式分為 HTTP Redirect Binding、HTTP POST Binding、HTTP Artifact Binding。每種方式在不同的階段會用不同類型的 HTTP 與對方通信。
SP 通過重定向 GET 請求把 SAML Request 發送到 IdP,IdP 通過立即提交的 Form 表單以 POST 請求的方式將 SAML Response 發到 SP。
IdP 通過立即提交的 Form 表單以 POST 請求的方式將 SAML Request 發到 SP。IdP 通過立即提交的 Form 表單以 POST 請求的方式將 SAML Response 發到 SP。
SP、IdP 雙方只通過瀏覽器交換 SAML Request、SAML Response 的索引編號,收到編號后,在后端請求對方的 Artifact Resolution Service 接口來獲取真正的請求實體內容。從而避免 SAML Request、SAML Response 暴露在前端。
準備兩臺服務器,一臺用作k8smaster,一臺用作k8snode。示例系統為Centos7。
IP | ROLE |
192.168.2.131 | k8smaster |
192.168.2.132 | k8snode |
每臺機器都需要執行如下命令
# 關閉防火墻
sudo systemctl stop firewalld
sudo systemctl disable firewalld
# 關閉selinux
# 永久 # 這是允許容器訪問主機文件系統所必需的
sudo sed -i 's/enforcing/disabled/' /etc/selinux/config
# 臨時
sudo setenforce 0
# 關閉swap
# 臨時
sudo swapoff -a
# 永久
sudo sed -ri 's/.*swap.*/#&/' /etc/fstab
# 根據規劃設置主機名
sudo hostnamectl set-hostname <hostname> #k8smaster k8snode
# 添加hosts
sudo cat >> /etc/hosts << EOF
192.168.2.131 k8smaster
192.168.2.132 k8snode
EOF
sudo cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables=1
net.bridge.bridge-nf-call-iptables=1
EOF
sysctl --system # 生效
# 時間同步
sudo yum install ntpdate -y
sudo ntpdate time.windows.com
date # 查看時間
sudo yum remove docker docker-common docker-selinux docker-engine
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 使用阿里云的docker安裝yum源
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo yum -y install docker-ce docker-ce-cli containerd.io
sudo systemctl enable docker
sudo systemctl start docker
每臺機器都需要
sudo cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
每臺機器都需要
sudo yum install -y kubelet-1.24.0 kubeadm-1.24.0 kubectl-1.24.0
sudo systemctl enable kubelet
在master節點執行
kubeadm init \
--apiserver-advertise-address=192.168.2.131 \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.24.0 \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=10.244.0.0/16
執行可能會報錯:
Kubeadm unknown service runtime.v1alpha2.RuntimeService
# 解決方法
# https://github.com/containerd/containerd/issues/4581
rm /etc/containerd/config.toml
systemctl restart containerd
輸出 Your Kubernetes control-plane has initialized successfully! 表示master節點安裝成功。
根據提示執行
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
在node節點執行
kubeadm join 192.168.2.131:6443 --token 9ypuou.dv1aqg0w3f4k5woe \
--discovery-token-ca-cert-hash sha256:4837358ffa2fce8e2a45be7a99b01b020ced7adf52c151ccdfc3b756108e3041
把node節點加入集群。輸出如下信息表示node節點加入集群成功。
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8smaster NotReady control-plane 13m v1.24.0 192.168.2.131 <none> CentOS Linux 7 (Core) 3.10.0-1160.el7.x86_64 containerd://1.6.7
k8snode NotReady <none> 49s v1.24.0 192.168.2.132 <none> CentOS Linux 7 (Core) 3.10.0-1160.el7.x86_64 containerd://1.6.7
目前節點狀態還是 NotReady,還需要配置網絡插件。
# 在主節點執行
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# 輸出
namespace/kube-flannel created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created
# 查看pod狀態
kubectl get pods -n kube-system
# 輸出
NAME READY STATUS RESTARTS AGE
coredns-74586cf9b6-fr5vk 0/1 Pending 0 16m
coredns-74586cf9b6-j8pxj 0/1 Pending 0 16m
etcd-k8smaster 1/1 Running 0 16m
kube-apiserver-k8smaster 1/1 Running 0 16m
kube-controller-manager-k8smaster 1/1 Running 0 16m
kube-proxy-6cpxh 1/1 Running 0 3m49s
kube-proxy-kbb82 1/1 Running 0 16m
kube-scheduler-k8smaster 1/1 Running 0 16m
等待一段時間coredns狀態ready之后即可。
最終所有pod狀態都處于Running。
至此,k8s單master集群已經安裝完成,接下來通過部署一個nginx服務測試下集群是否可用。
master 執行
kubectl create deployment nginx --image=nginx
# 等待一段時間查看pod狀態
kubectl get pod -o wide
pod狀態為Running表示nginx已經部署完成。想要訪問nginx服務還需要暴露外部訪問端口
kubectl expose deployment nginx --port=80 --target-port=80 --type=NodePort
# 輸出
service/nginx exposed
# 查看暴露的端口
kubectl get svc
svc是k8s里面的一種服務,用于pod訪問。port是svc的端口,target-port是容器的端口,把port改為12345也是可行的,可以一試。以后有時間具體講解。
通過集群的任一ip的30779端口即可訪問到該nginx服務。