Базовые знания о едином входе

сервер продукт

Что такое единый вход

войти(Single Sign On), именуемый SSO, является одним из наиболее популярных решений для корпоративной бизнес-интеграции. Определение SSO заключается в том, что в системах с несколькими приложениями пользователям необходимо войти в систему только один раз, чтобы получить доступ ко всем взаимно доверенным системам приложений.

Для SSO обычно требуется независимый центр аутентификации (паспорт), а вход в подсистемы должен проходить через паспорт. Сама подсистема не будет участвовать в операции входа. При успешном входе в систему паспорт выдает токен каждому подсистема.Подсистемы могут получать свои собственные защищенные ресурсы с помощью токенов.Чтобы уменьшить частую аутентификацию, каждая подсистема будет устанавливать локальный сеанс после авторизации по паспорту, и нет необходимости повторно инициировать аутентификацию по паспорту в течение определенного периода времени.

Например, Taobao и Tmall являются продуктами, принадлежащими Alibaba. Когда пользователь входит в систему Taobao, а затем открывает Tmall, система автоматически выполняет вход в Tmall для пользователя. Это явление реализовано с помощью единого входа.

Процесс единого входа

1. Войти

  • Пользователь обращается к защищенным ресурсам системы 1, а система 1 обнаруживает, что пользователь не вошел в систему, переходит к центру аутентификации sso и использует собственный адрес в качестве параметра
  • Центр аутентификации sso обнаруживает, что пользователь не вошел в систему, и направляет пользователя на страницу входа.
  • Пользователь вводит имя пользователя и пароль для подачи заявки на вход
  • Центр аутентификации sso проверяет информацию о пользователе, создает сеанс между пользователем и центром аутентификации sso, называемый глобальным сеансом, и одновременно создает токен авторизации.
  • Центр аутентификации sso отправляет токен на начальный адрес запроса (система 1).
  • Система 1 получает токен и обращается в центр аутентификации sso, чтобы проверить, действителен ли токен.
  • Центр аутентификации SSO проверяет токен, возвращает действительный и регистрирует систему 1.
  • Система 1 использует этот токен для создания сеанса с пользователем, называемого частичным сеансом, возвращая защищенный ресурс.
  • Пользователь получает доступ к защищенным ресурсам Системы 2
  • Система 2 обнаруживает, что пользователь не вошел в систему, переходит к центру аутентификации sso и использует собственный адрес в качестве параметра.
  • Центр аутентификации sso обнаруживает, что пользователь вошел в систему, возвращается к адресу системы 2 и прикрепляет токен.
  • Система 2 получает токен и обращается в центр аутентификации sso, чтобы проверить, действителен ли токен.
  • Центр аутентификации sso проверяет токен, возвращает действительный и регистрирует систему 2.
  • Система 2 использует токен для создания частичного сеанса с пользователем, возвращая защищенный ресурс.

После успешного входа пользователя в систему будет установлен сеанс с центром аутентификации SSO и каждой подсистемой. Сеанс, установленный между пользователем и центром аутентификации SSO, называется глобальным сеансом, а сеанс, установленный пользователем и каждой подсистемой, называется локальный сеанс.После того, как локальный сеанс установлен, пользователь получает доступ к защищенным ресурсам подсистемы, которые больше не будут проходить через центр аутентификации sso, а глобальный сеанс и локальный сеанс имеют следующие ограничения.

  • Локальный сеанс существует, глобальный сеанс должен существовать
  • Глобальная сессия существует, но локальная сессия не обязательно существует
  • Глобальная сессия уничтожена, локальная сессия должна быть уничтожена
2. Выйти

Центр аутентификации sso отслеживает состояние глобального сеанса. После уничтожения глобального сеанса прослушиватель уведомит все системы регистрации о необходимости выполнения операции выхода из системы.

  • Пользователь инициирует запрос на выход из системы 1.
  • Система 1 получает токен в соответствии с идентификатором сеанса, установленным пользователем и системой 1, и инициирует запрос на выход из системы в центр аутентификации sso.
  • Центр аутентификации sso проверяет допустимость токена, уничтожает глобальный сеанс и удаляет все системные адреса, зарегистрированные с этим токеном.
  • Центр сертификации SSO инициирует запрос на выход из системы во все системы регистрации.
  • Каждая система регистрации получает запрос на выход из центра аутентификации sso и уничтожает локальную сессию.
  • Центр аутентификации SSO направляет пользователя на страницу входа.

Что такое КАС

CAS — это сокращение от Central Authentication Service, центральной службы аутентификации, независимого открытого командного протокола. CAS — это проект с открытым исходным кодом, инициированный Йельским университетом для предоставления надежного метода единого входа для веб-приложений. CAS состоит из двух частей: CAS Server и CAS Client. CAS-сервер должен быть развернут независимо и в основном отвечает за аутентификацию пользователей; CAS-клиент отвечает за обработку запросов на доступ к защищенным ресурсам клиента и перенаправляет на CAS-сервер при входе в систему.

Самый простой процесс протокола CAS:

Клиент CAS развертывается с защищенным клиентским приложением для защиты защищенных ресурсов в режиме фильтра. Для каждого веб-запроса на доступ к защищенным ресурсам клиент CAS будет анализировать, содержит ли Http-запрос запроса сервисный билет, если нет, это означает, что текущий пользователь не вошел в систему, поэтому запрос будет перенаправлен на указанный сервер CAS. адрес входа в систему и передать службу (то есть адрес ресурса назначения, к которому необходимо получить доступ), чтобы его можно было передать обратно на адрес после успешного входа в систему. Пользователь вводит информацию для аутентификации на шаге 3. Если вход в систему выполнен успешно, CAS-сервер случайным образом генерирует служебный билет значительной длины, уникальный и не поддающийся подделке, и кэширует его для будущей проверки Браузер терминала устанавливает файл Cookie Granted (TGC) , После того, как клиент CAS получит услугу и вновь сгенерированный билет, он проверит личность на сервере CAS на шагах 5 и 6, чтобы убедиться в действительности билета службы. В этом протоколе все взаимодействия с CAS используют протокол SSL для обеспечения безопасности ST и TGC. В процессе работы протокола будет 2 процесса перенаправления, но процесс проверки билетов между CAS Client и CAS Server прозрачен для пользователей. Кроме того, протокол CAS также предоставляет режим прокси для адаптации к более продвинутым и сложным сценариям приложений.Подробности см. в соответствующих документах на официальном сайте CAS.

Что такое OAuth2

OAuth (открытая авторизация) — это открытый стандарт, который позволяет пользователю разрешать сторонним приложениям получать доступ к личным ресурсам (таким как фотографии, видео, списки контактов), хранящимся у пользователя на веб-сайте, без предоставления имени пользователя и пароля третьим лицам. партийное приложение.

С точки зрения непрофессионала, OAuth — это протокол авторизации. Пока авторизирующая сторона и авторизованная сторона соблюдают этот протокол для написания кода для предоставления услуг, обе стороны реализуют режим OAuth.

В частности, OAuth устанавливает уровень авторизации между «клиентом» и «поставщиком услуг». «Клиент» не может напрямую войти в «поставщика услуг», только уровень авторизации, который отличает пользователя от клиента. Токен, используемый «клиентом» для входа на уровень авторизации, отличается от пароля пользователя. Пользователь может указать область разрешений и срок действия токена уровня авторизации при входе в систему. После того, как «клиент» входит в систему на уровне авторизации, «поставщик услуг» открывает «клиенту» сохраненные данные пользователя в соответствии с областью разрешений и сроком действия токена.

OAuth2 — это следующая версия OAuth1.0. OAuth2 ориентирован на простоту для разработчиков клиентов, обеспечивая при этом специализированные потоки проверки подлинности для веб-приложений, настольных приложений, мобильных устройств и устройств в жилых комнатах. Исходный OAuth выдает токен с очень длительным периодом действия (обычно срок действия один год или без ограничения срока действия).В OAuth 2.0 сервер выдает токен доступа с коротким сроком действия и токен обновления с длительным сроком действия. Это позволит клиенту получить новый токен доступа, и пользователю не придется делать это снова, а также ограничит срок действия токена доступа.

Разница между CAS и OAuth2

  • Единый вход CAS гарантирует безопасность пользовательских ресурсов клиента, а OAuth2 гарантирует безопасность пользовательских ресурсов сервера;
  • Окончательная информация, которую должен получить клиент CAS, заключается в том, есть ли у этого пользователя разрешение на доступ к моим (клиента CAS) ресурсам; окончательная информация, которую получает oauth2, заключается в том, могут ли ресурсы моего (поставщика услуг oauth2) пользователя разрешить вам (клиент oauth2) доступ;
  • При едином входе CAS ресурсы находятся на стороне клиента, а не на стороне сервера CAS. После того, как пользователь предоставляет имя пользователя и пароль серверу CAS, клиент CAS об этом не знает. Если я даю клиенту ЗБ, то клиент не может определить, подделано ли ЗБ пользователем или действительно действительно, поэтому я должен отнести это ЗБ на сервер и снова спросить, дал ли мне пользователь действительное ЗБ или недействительный ST, действителен до того, как я смогу предоставить этому пользователю доступ.
  • Аутентификация OAuth2, ресурсы находятся на стороне поставщика услуг OAuth2, и клиент хочет запросить ресурсы пользователя. Поэтому в самом безопасном режиме, после авторизации пользователя, сервер не может напрямую вернуть токен и отправить его клиенту через перенаправление, потому что токен может быть перехвачен хакерами.Если хакер перехватит токен, ресурсы пользователя также будут подвергается этому взлому. Таким образом, смарт-сервер отправляет код аутентификации клиенту (через перенаправление), а клиент находится в фоновом режиме, через https, используя этот код и другую строку предварительно согласованных паролей между клиентом и сервером для получения токена To и обновления. токен, этот процесс очень безопасен. Если хакер перехватит код, он не сможет получить токен без предварительно согласованного пароля. Таким образом, oauth2 может гарантировать, что запрос ресурсов согласован пользователем, а клиент также распознан, поэтому ресурс может быть отправлен клиенту с уверенностью.
  • Самая большая разница между входом в CAS и OAuth2 заключается в том, что при аутентификации через ST или код вам не нужен заранее согласованный пароль.
Суммировать:
CAS: сервер авторизации, авторизованный клиент
  1. Сервер авторизации (один) сохраняет глобальную сессию, а клиент (несколько) сохраняет каждый свою сессию;
  2. Когда клиент входит в систему, он определяет, был ли выполнен вход в его сеанс. Если он не вошел в систему, он (указывает браузеру) перенаправить на сервер авторизации (параметр берет свой собственный адрес для обратного вызова);
  3. Сервер авторизации определяет, был ли выполнен вход в глобальный сеанс. Если нет, он будет перенаправлен на страницу входа, предлагая пользователю войти в систему. После успешного входа сервер авторизации перенаправит клиента (с билетом [ учетный номер] в параметре);
  4. После того, как клиент получает билет, он запрашивает у сервера информацию о пользователе;
  5. После того, как сервер соглашается на авторизацию клиента, сервер сохраняет информацию о пользователе в глобальном сеансе, а клиент сохраняет пользователя в локальном сеансе.
OAuth2: основная система, система авторизации (для авторизации основной системы также может быть та же система, что и основная система), сторонняя система
  1. Сторонней системе нужно использовать ресурсы основной системы, а сторонняя перенаправляет на систему авторизации;
  2. В соответствии с различными методами авторизации система авторизации предлагает пользователю авторизоваться;
  3. После авторизации пользователя система авторизации возвращает сертификат авторизации (accessToken) в стороннюю систему [accessToken действителен];
  4. Третья сторона использует accessToken для доступа к основным системным ресурсам [после истечения срока действия accessToken третьей стороне необходимо повторно запросить систему авторизации для получения нового accessToken].

Что такое JWT

JSON Web Token (JWT) — это открытый стандарт (RFC 7519), определяющий компактный и автономный способ безопасной передачи информации между сторонами в виде объектов JSON. Эта информация может быть проверена и доверена с помощью цифровых подписей. JWT могут быть подписаны секретом (с использованием алгоритма HMAC) или парой открытого/закрытого ключа с использованием RSA или ECDSA.

Структура токена JSON WEB состоит из трех частей:

  • Заголовок: укажите тип токена и используемый алгоритм хеширования.
  • Полезная нагрузка: утверждение — это утверждение об объекте (обычно о пользователе) и других данных. Существует три типа претензий: стандартные зарегистрированные претензии, публичные претензии и частные претензии.
  • Подпись: Должен взять закодированный заголовок, закодированную полезную нагрузку, секрет, алгоритм, указанный в заголовке, и подписать его.
  1. Полезная нагрузка — стандартное заявление:
  • iss: эмитент JWT
  • sub: пользователь, для которого предназначен JWT
  • aud: сторона, получающая JWT
  • exp: время истечения срока действия JWT, это время истечения должно быть больше, чем время выдачи, которое равно количеству секунд.
  • nbf: определяет время до недоступности JWT.
  • iat: время выдачи JWT
  1. Полезная нагрузка — публичное заявление: вы можете добавить любую информацию, как правило, информацию, связанную с пользователем, или другую необходимую информацию для нужд бизнеса, но не рекомендуется добавлять конфиденциальную информацию, поскольку эта часть может быть расшифрована на стороне клиента.
  2. Полезная нагрузка - частный оператор: оператор, совместно определенный поставщиком и потребителем.Как правило, не рекомендуется хранить конфиденциальную информацию, поскольку base64 расшифровывается симметрично, что означает, что эта часть информации может быть классифицирована как информация открытого текста.

Создание подписи требует использования закодированногоheaderиpayloadи секретный ключ, используяheaderУкажите алгоритм подписи для подписи. Например, если вы хотите использоватьHMAC SHA256алгоритм, то подпись должна быть создана с использованиемHMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload), secret)Подпись используется для проверки отправителя сообщения и того, что сообщение не было подделано. Полный вывод в формате JWT:.Раздельная трехсегментная кодировка Base64, Секрет ключа хранится на сервере, и сервер сгенерирует его на основе этого ключаtokenи проверка, поэтому она должна быть хорошо защищена, для получения дополнительной информации, пожалуйста, переместитеОфициальный сайт

Единый вход в интерфейсе

Этот код использует OAuth2. оtokenВ случае проблем с хранением обратитесь ко многим учебным пособиям в Интернете, большинство из которыхtokenсохранить вcookie, тоcookieУстановите домен верхнего уровня для решения междоменных проблем, но наши бизнес-потребности заключаются в том, что некоторые домены верхнего уровня продукта также отличаются. Таким образом, идея реализации состоит в том, чтобыtokenсохранить вlocalStorage, затем передайте новый атрибут H5postMessageЧтобы добиться междоменного обмена, вы можете увидеть меня, если вы не знаете о междоменномэта статья.

Идея реализации: когда пользователь обращается к системе компании (такой как product.html), в продукте сначала загружается iframe, а в iframe можно получить токен, хранящийся в localStorage.Пользователь будет перенаправлен на страница входа, пользователь войдет на эту страницу и все равно перейдет в систему аутентификации, чтобы получить токен и сохранить его в локальном хранилище страницы iframe.

<!--product.html-->
<head>
    <script src="auth_1.0.0.js"></script>
</head>
<body>
    <h2>产品页面</h2>
    <a onClick="login()" id="login">登录</a>
    <h3 id="txt"></h3>
</body>
<script>
var opts = {
    origin: 'http://localhost:8080',
    login_path: '/login.html',
    path: '/cross_domain.html'
}
// 加载iframe,将src值为cross_domain.html的iframe加载到本页
var auth = new ssoAuth(opts);
function getTokenCallback(data) {
    //如果没有token则跳到登录页
    if(!data.value){
        auth.doWebLogin();
    }
    //如果有token,直接在页面显示,然后做其它操作
    document.getElementById('txt').innerText = 'token=' + data.value;
}
// 获取存储在名为cross_domain的iframe中的token
auth.getToken(getTokenCallback);
</script>

Объяснение. После создания экземпляра ssoAuth в product.html эта страница вводит iframe в текущую страницу со значением opts.path, то есть cross_domain.html. auth.getToken() должен получить значение localStorage на этой странице iframe.

//auth_1.0.0.js
function ssoAuth(opts) {
    this._origin = opts.origin,
    this._iframe_path = opts.path,
    this._iframe = null,
    this._iframe_ready = false,
    this._queue = [],
    this._auth = {},
    this._access_token_msg = { type: "get", key: "access_token" },
    this._callback = undefined,
    that = this;
    
    //判断是否支持postMessage及localStorage
   var supported = (function () {
        try {
            return window.postMessage && window.JSON && 'localStorage' in window && window['localStorage'] !== null;
        } catch (e) {
            return false;
        }
    })();
    
    _iframeLoaded = function () {
        that._iframe_ready = true
        if (that._queue.length) {
            for (var i = 0, len = that._queue.length; i < len; i++) {
                _sendMessage(that._queue[i]);
            }
            that._queue = [];
        }
    }

    _sendMessage = function (data) {
        // 通过contentWindow属性,脚本可以访问iframe元素所包含的HTML页面的window对象。
        that._iframe.contentWindow.postMessage(JSON.stringify(data), that._origin);
    }
    
    //获取token,但因为此时iframe还没有加载完成,先将消息存储在队列_queue中
    this._auth.getToken = function (callback) {
        that._callback = callback
        if (that._access_token_msg && that._iframe_ready) {
            //当iframe加载完成,给iframe所在的页面发送消息
            _sendMessage(that._access_token_msg);
        } else {
            that._queue.push(that._access_token_msg);
        }
    }

    var _handleMessage = function (event) {
        if (event.origin === that._origin) {
            var data = JSON.parse(event.data);
            if (data.error) {
                console.error(event.data)
                that._callback({ value: null });
                return;
            }
            if (that._callback && typeof that._callback === 'function') {
                that._callback(data);
            } else {
                console.error("callback is null or not a function, please ");
            }
        }
    }

    this._auth.doWebLogin = function () {
        window.location.href = opts.origin + opts.login_path + "?redirect_url=" + window.location.href
    }
    //初始化了一个iframe,并追加到父页面的底部
    if (!this._iframe && supported) {
        this._iframe = document.createElement("iframe");
        this._iframe.style.cssText = "position:absolute;width:1px;height:1px;left:-9999px;";
        document.body.appendChild(this._iframe);

        if (window.addEventListener) {
            this._iframe.addEventListener("load", function () {
                _iframeLoaded();
            }, false);
            window.addEventListener("message", function (event) {
                _handleMessage(event)
            }, false);
        } else if (this._iframe.attachEvent) {
            this._iframe.attachEvent("onload", function () {
                _iframeLoaded();
            }, false);
            window.attachEvent("onmessage", function (event) {
                _handleMessage(event)
            });
        }
        this._iframe.src = this._origin + this._iframe_path;
    }
    return this._auth;
}
<!--cross_domain.html-->
<script type="text/javascript">
    (function () {

        //白名单
        var whitelist = ["localhost", "127.0.0.1", "^.*\.domain\.com"];

        function verifyOrigin(origin) {
            var domain = origin.replace(/^https?:\/\/|:\d{1,4}$/g, "").toLowerCase(),
                i = 0,
                len = whitelist.length;

            while (i < len) {
                if (domain.match(new RegExp(whitelist[i]))) {
                    return true;
                }
                i++;
            }
            return false;
        }

        function handleRequest(event) {
            // 白名单较验
            if (verifyOrigin(event.origin)) {
                var request = JSON.parse(event.data);
                if (request.type == 'get') {
                    var idi = localStorage.getItem("idi");
                    if (!idi) {
                        // source:对发送消息的窗口对象的引用,event.source只是window对象的代理,不能通过它访问window//的其它信息
                        event.source.postMessage(JSON.stringify({ key: request.key, value: null }), event.origin);
                        return;
                    }
                    value = JSON.parse(idi)[request.key];
                    event.source.postMessage(JSON.stringify({ key: request.key, value: value }), event.origin);
                } else {
                    event.source.postMessage(JSON.stringify({ error: "Not supported", error_description: "Not supported message type" }), event.origin);
                }
            }
        }
        // 接收iframe传来的消息
        if (window.addEventListener) {
            window.addEventListener("message", handleRequest, false);
        } else if (window.attachEvent) {
            window.attachEvent("onmessage", handleRequest);
        }
    })();
</script>
<!--login.html-->
<head>
    <script src="auth_1.0.0.js"></script>
</head>
<body>
    <form>
        <input type="text" placeholder="用户名" id="user">
        <input type="password" placeholder="密码" id="pwd">
    </form>
    <button onClick="login()">登 录</button>
</body>
<script>
    function login() {
        var name = document.getElementById('user')
        var pwd = document.getElementById('pwd')

        var expires_in = 7200
        //假如这是登录成功后,后台开发人员返回的json数据
        var res = { 
            access_token: "xxxxx.yyyyy.zzzzz", 
            expires_at: expires_in * 1000 + new Date().getTime(), 
            refresh_token: "yyyyyyyyyyyyyyyyyyyyyyyyyyyy" 
        };
        localStorage.setItem("idi", JSON.stringify(res))
        //登录成功后再返回原页面
        window.location.href = getQueryString("redirect_url")
    }

    function getQueryString(name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
        var r = window.location.search.substr(1).match(reg);
        if (r != null) return unescape(r[2]); return null;
    }
</script>

P.S. Отмены пока не было. Кроме того, у postMessage есть проблемы с совместимостью. Если у других друзей есть лучшие методы, поделитесь ими, спасибо~

Ссылаться на: