Pull to refresh

Введение в Claims-based identity

Reading time5 min
Views32K
При разработке приложений на стеке Microsoft для получения информации о текущем пользователе достаточно часто(точнее почти всегда) можно встретить такие участки кода или обертки над ними:

HttpContext.User.Identity.Name
HttpContext.User.IsInRole(...)


или

Thread.CurrentPrincipal.Identity.Name
Thread.CurrentPrincipal.IsInRole(...)


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

Информация о пользователе появлялась в этих классах разными способами: чтение данных из БД, Forms authentication, NTLM token, Kerberos token. В каждом конкретном случае решалась задача получения информации о пользователе, его аутентификации и получения дополнительной информации.

В “дооблачные времена” этого было вполне достаточно для большинства приложений. Если этого не хватало, то создавались разного рода собственные фреймворки, но во главе угла зачастую стоял главный вопрос: обладает ли пользователь определенной ролью. До определенного момента этого хватало пока хранилище пользователей было одно, не было необходимости взаимодействовать с партнерами по бизнесу и тд. С появлением облаков, распределенных систем, SaaS приложений и других плюшек без которых трудно себе представить современный веб, этой модели стало не хватать, если, к примеру, вы захотели разрешить сотрудникам вашего партнера доступ к определенным функциям вашей CRM. Так же часто встает вопрос развития и эволюционирования приложения, например: изначально вы планировали использовать две группы пользователей User и Administrator и в своем коде щедро раставили авторизационные
атрибуты вида:

[Authorize("Administrators")]
public ActionResult DoSomeHardcoreAdminStuff()
{
...
}


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

С точки зрения разработчика, все это выливалось в зоопарк технологий и костылей. Каждое приложение аутентифицировало пользователей по-своему. Пользователь мог аутентифицироваться используя OAuth, Forms, Windows или что-нибудь еще. В каждом конкретном случае приходилось писать свою логику аутентификации и авторизации, а если у вас был Api, то еще и для него велосипедик дорисовать.

В ответ на это в 2008 году из недр Microsoft увидел свет первый релиз Windows Identity Foundation(WIF) и была представлена концепция Claims-based identity. Целью этого фреймворка является предоставление абстрактного механизма выражения своих требований к пользователю не углубляясь в детали того, как это работает.

Вкратце идею WIF можно описать достаточно простым жизненным примером:
Вам исполнилось 18 и вы решили сходить в кино. Взрослое кино. Но, к сожалению, не успели получить паспорт или любое другое удостоверение личности до сих пор(ну или просто было лень). Вы собираетесь и идете в паспортный стол, через какое-то время получаете паспорт и, предъявляя ваш паспорт, смело покупаете себе заветный билетик и идете на сеанс. А вот так это выглядит с точки зрения WIF:



Subject, тоесть вы, идете к Identity provider(паспортный стол) и на основе Свидетельство_о_рожденииToken получаете ПасспортToken. Потом, вместе с этим ПасспортToken вы идете к Relying party(кинотеатр) и, после подтверждения вашего возраста, получаете доступ к услуге.

Основные идеи которые можно извлечь из этого примера:

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

2. Паспортный стол не знает где вы будете предъявлять паспорт. (С точки зрения WIF все таки чуточку знать должен, но это не обязательно).

3. С паспортом вы можете купить себе хорошего виски после похода в кино, взять ипотеку или еще что-то в любом учреждении, которое доверяет документам, выданным гос. учреждением.

Кто то уже работал с такими протоколами как OAuth, WS-Trust и WS-Fed, SAML-P и эта схема взаимодействия будет им знакома. Вкратце — информацию о пользователе вы получаете от доверенной удостоверяющей стороны(Identity provider) в виде токена определенного формата и используете ее для принятия каких-либо решений в вашем приложении. В вырожденном случае, например Forms authentication вы сами являетесь этой удостоверяющей стороной и сами же используете эту информацию. WIF допускает такие сценарии. WIF достаточно гибок для поддержки самого разного рода сценариев.

WIF позволяет “аутсорсить” процесс аутентификации доверенной стороне и позволяет свести к минимуму необходимость вмешательство разработчика в процесс аутентификации и авторизации. Все удостоверения, которые предъявляются вашему приложению приводятся к типам ClaimsPrincipal и ClaimsIdentity. Эти типы очень похожи на стандартные *Principal и *Identity, так же реализуют интерфейсы IPrincipal и IIdentity, но имеют дополнительное свойство, которое является коллекцией всех утверждений которые доступны вам о текущем пользователе. Причем для совместимости поддерживаются различные существующие способы работы с IIdentity и IClaimsPrincipal, например:

[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
static void CheckAdministrator()
{
Console.WriteLine("User is an administrator");
}


Для этого достаточно что бы у пользователя было утверждение типа роль( можно настраивать тип утверждений которые будут использоваться как роли) со значением “Administrators”.

В приложение на ASP.NET MVC это может выглядеть так:

[ClaimsAuthorize(ClaimTypes.Role, "Administrators")]
public ActionResult DoSomeHardcoreAdminStuff()
{
...
}


Или так:

[ClaimsAuthorize(ClaimTypes.Permission, "DoSomeHardcoreAdminStuff")]
public ActionResult DoSomeHardcoreAdminStuff()
{
...
}


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

В результате всех этих трансформаций вашему приложению больше не навязывается Role based security подход и вы вольны сами выбирать как, на основание чего и где проводить необходимые проверки, а так же, в части случаев, полностью избавиться от механизмов хранения информации о пользователе внутри приложения. Кроме всего прочего, вы не заботитесь о том, каким способом пользователь прошел аутентификацию, будь это стандартная пара логин-пароль или хитрая смарт карта. Это задача вашего Identity Provider.

На данный момент на платформах от Microsoft есть два основных решения для такого подхода: ADFS(Active Directory Federation Services) и Azure ACS. Если вам не подходит ни то ни другое, то вы вольны самостоятельно написать свой сервис, благо из коробки в студию ставится шаблон с примером. Так же есть опенсорсный сервер IdentityServer на основе которого можно развивать свой собственный продукт.

Немного фактов:

Из коробки WIF поддерживает следующие протоколы:
1. WS-Federation
2. WS-Trust
3. WS-Security
4. WS-SecurityPolicy
5. WS-Addressing

Поддержка протокола SAML-P находится в состоянии CTP. Информации о RTM версии пока нет. Так же есть OAuth2 extensions.

Стандартно поддерживаются удостоверения SAML1.1 и SAML2. Но уже есть достаточно развитые библиотеки которые добавляют поддержку SWT и даже JWT(Json Web Token).

Это был очень маленький экскурс в то, что происходит в пространстве имен System.Security. В рамках вводного поста не хотелось бы вдаваться в подробности
Кстати в .Net 4.5 Claims-based identity и WIF становятся королем горы. Все типы *Principal будут унаследованы от ClaimsPrincipal, Kerberos токены внутри будут содержать набор утверждений и много чего еще вкусного. Если кому-то интересна эта тема, пишите ваши пожелания в комментариях, как будет время обязательно постараюсь написать.

Полезные ссылки:
1. msdn.microsoft.com/en-us/security/aa570351.aspx — страница на MSDN.
2. claimsid.codeplex.com — примеры кода и бесплатная книжка.
3. leastprivilege.com — блог Dominick Baier.
4. github.com/thinktecture/Thinktecture.IdentityServer — open source Identity Server.

PS. Спасибо XaocCPS за рецензию.
Tags:
Hubs:
Total votes 21: ↑19 and ↓2+17
Comments55

Articles