ASP.NET MVC Урок 1-F / ASP.NET MVC Урок 6
.pdf$63 1(7 09& ǻȘȖȒ ǨȊȚȖȘȐȏȈȞȐȧ tutorial
ASP*, .NET*
Цель урока: Изучить способ авторизации через Cookie, использование стандартных атрибутов доступа к контроллеру и методу контроллера. Использование IPrincipal. Создание собственного модуля (IHttpModule) и собственного фильтра IActionFilter.
Небольшое отступление: На самом деле в asp.net mvc все учебники рекомендуют пользоваться уже придуманной системой авторизации, которая называется AspNetMembershipProvider, она была описана в статьеhttp://habrahabr.ru/post/142711/ (сейчас доступ уже закрыт), но обьяснено это с точки зрения «нажимай и не понимай, что там внутри». При первом знакомстве с asp.net mvc меня это смутило. Далее, в этой статьеhttp://habrahabr.ru/post/143024/ — сказано, что пользоваться этим провайдером – нельзя. И я согласен с этим. Здесь же, мы достаточно глубоко изучаем всякие хитрые asp.net mvc стандартные приемы, так что это один из основных уроков.
Кукисы
Кукисы – это часть информации, отсылаемая сервером браузеру, которую браузер возвращает обратно серверу вместе с каждым (почти каждым) запросом.
Сервер в заголовок ответа пишет:
6HW &RRNLH YDOXH>H[SLUHV GDWH@>GRPDLQ GRPDLQ@>SDWK SDWK@>VHFXUH@
Например:
+773 2. &RQWHQW W\SH WH[W KWPO 6HW &RRNLH QDPH YDOXH
6HW &RRNLH QDPH YDOXH ([SLUHV :HG-XQ *07
Браузер (если не истекло время действия кукиса) при каждом запросе:
*(7VSHF KWPO+773
+RVW ZZZ H[DPSOH RUJ
&RRNLH QDPH YDOXH QDPH YDOXH
$FFHSW
Устанавливаем cookie (/Areas/Default/Controllers/HomeController.cs):
SXEOLF $FWLRQ5HVXOW,QGH[
^
YDUFRRNLH QHZ+WWS&RRNLH
^
1DPH WHVWBFRRNLH
9DOXH 'DWH7LPH 1RZ 7R6WULQJGG 00 \\\\
([SLUHV 'DWH7LPH 1RZ$GG0LQXWHV
`
5HVSRQVH 6HW&RRNLH FRRNLH
UHWXUQ9LHZ
`
В Chrome проверяем установку:
Для получения кукисов:
YDUFRRNLH 5HTXHVW &RRNLHV>WHVWBFRRNLH@
Делаем точку остановки и проверяем:
Примечание: подробнее можно изучить кукисы по следующей ссылке: http://www.nczonline.net/blog/2009/05/05/httpcookiesexplained/
Авторизация
В нашем случае авторизация будет основана на использовании кукисов. Для этого изучим следующие положения:
FormsAuthenticationTicket – мы воспользуемся этим классом, чтобы хранить данные авторизации в зашифрованном виде
Нужно реализовать интерфейс IPrincipal и установить в+WWS&RQWH[W 8VHU для проверки ролей и ,,GHQWLW\ интерфейса.
Для интерфейса IIdentity сделать реализацию
Вывести в BaseController в свойство CurrentUser значение пользователя, который сейчас залогинен.
Приступим.
Создадим интерфейс IAuthentication и его реализацию CustomAuthentication (/Global/Auth/IAuthentication.cs):
SXEOLF LQWHUIDFH ,$XWKHQWLFDWLRQ
^
VXPPDU\!
Ʉɨɧɟɤɫɬ ɬɭɬ ɦɵ ɩɨɥɭɱɚɟɦ ɞɨɫɬɭɩ ɤ ɡɚɩɪɨɫɭ ɢ ɤɭɤɢɫɚɦ
VXPPDU\!
+WWS&RQWH[W +WWS&RQWH[W ^JHW VHW`
8VHU/RJLQ VWULQJORJLQVWULQJSDVVZRUGERROLV3HUVLVWHQW
8VHU/RJLQ VWULQJORJLQ
YRLG /RJ2XW
,3ULQFLSDO &XUUHQW8VHU ^JHW`
`
Реализация (/Global/Auth/CustomAuthentication.cs):
SXEOLF FODVV &XVWRP$XWKHQWLFDWLRQ ,$XWKHQWLFDWLRQ
^
SULYDWH VWDWLF1/RJ /RJJHU ORJJHU 1/RJ /RJ0DQDJHU *HW&XUUHQW&ODVV/RJJHU
SULYDWH FRQVW VWULQJFRRNLH1DPH BB$87+B&22.,(
SXEOLF+WWS&RQWH[W +WWS&RQWH[W ^JHW VHW`
>,QMHFW@
SXEOLF,5HSRVLWRU\ 5HSRVLWRU\ ^JHW VHW`
UHJLRQ ,$XWKHQWLFDWLRQ 0HPEHUV
SXEOLF8VHU/RJLQ VWULQJXVHU1DPHVWULQJ3DVVZRUGERROLV3HUVLVWHQW
^
8VHU UHW8VHU 5HSRVLWRU\ /RJLQ XVHU1DPH 3DVVZRUG
LIUHW8VHU QXOO
^
&UHDWH&RRNLH XVHU1DPH LV3HUVLVWHQW
`
UHWXUQUHW8VHU
`
SXEOLF8VHU/RJLQ VWULQJXVHU1DPH
^
8VHU UHW8VHU 5HSRVLWRU\ 8VHUV )LUVW2U'HIDXOW S !VWULQJ&RPSDUH S (PDLO XVHU1DPH WUXH
LIUHW8VHU QXOO
^
&UHDWH&RRNLH XVHU1DPH
`
UHWXUQUHW8VHU
`
SULYDWH YRLG &UHDWH&RRNLH VWULQJXVHU1DPHERROLV3HUVLVWHQW IDOVH
^
YDUWLFNHW QHZ)RUPV$XWKHQWLFDWLRQ7LFNHW
XVHU1DPH
'DWH7LPH 1RZ
'DWH7LPH 1RZ$GG )RUPV$XWKHQWLFDWLRQ 7LPHRXW
LV3HUVLVWHQW
VWULQJ(PSW\
)RUPV$XWKHQWLFDWLRQ )RUPV&RRNLH3DWK
(QFU\SW WKH WLFNHW
YDUHQF7LFNHW )RUPV$XWKHQWLFDWLRQ (QFU\SW WLFNHW
&UHDWH WKH FRRNLH
YDU $XWK&RRNLH QHZ+WWS&RRNLH FRRNLH1DPH
^
9DOXH HQF7LFNHW
([SLUHV 'DWH7LPH 1RZ$GG )RUPV$XWKHQWLFDWLRQ 7LPHRXW
`
+WWS&RQWH[W 5HVSRQVH &RRNLHV 6HW$XWK&RRNLH
`
SXEOLF YRLG /RJ2XW
^
YDUKWWS&RRNLH +WWS&RQWH[W 5HVSRQVH &RRNLHV>FRRNLH1DPH@
LIKWWS&RRNLH QXOO
^
KWWS&RRNLH 9DOXH VWULQJ(PSW\
`
`
SULYDWH,3ULQFLSDO BFXUUHQW8VHU
SXEOLF,3ULQFLSDO &XUUHQW8VHU
^
JHW
^
LIBFXUUHQW8VHU QXOO
^
WU\
^
+WWS&RRNLH DXWK&RRNLH +WWS&RQWH[W 5HTXHVW &RRNLHV *HW FRRNLH1DPH
LIDXWK&RRNLH QXOO VWULQJ,V1XOO2U(PSW\ DXWK&RRNLH 9DOXH
^
YDUWLFNHW )RUPV$XWKHQWLFDWLRQ 'HFU\SW DXWK&RRNLH 9DOXH
BFXUUHQW8VHU QHZ8VHU3URYLGHU WLFNHW 1DPH 5HSRVLWRU\
`
HOVH
^
BFXUUHQW8VHU QHZ8VHU3URYLGHUQXOO QXOO
`
`
FDWFK([FHSWLRQ H[
^
ORJJHU (UURU)DLOHG DXWKHQWLFDWLRQH[ 0HVVDJH
BFXUUHQW8VHU QHZ8VHU3URYLGHUQXOO QXOO
`
`
UHWXUQBFXUUHQW8VHU
`
`
HQGUHJLRQ
`
Суть сводится к следующему, мы, при инициализации запроса, получаем доступ к +WWS&RQWH[W 5HTXHVW &RRNLHV и инициализируем 8VHU3URYLGHU:
YDUWLFNHW )RUPV$XWKHQWLFDWLRQ 'HFU\SW DXWK&RRNLH 9DOXH
BFXUUHQW8VHU QHZ8VHU3URYLGHU WLFNHW 1DPH 5HSRVLWRU\
Для авторизации в IRepository добавлен новый метод IRepository.Login. Реализация в
SqlRepository:
SXEOLF8VHU/RJLQ VWULQJHPDLOVWULQJSDVVZRUG
^
UHWXUQ'E 8VHUV )LUVW2U'HIDXOW S !VWULQJ&RPSDUH S (PDLO HPDLOWUXH S 3D VVZRUG SDVVZRUG
`
UserProvider, собственно, реализует интерфейс IPrincipal (в котором есть проверка ролей и доступ к IIdentity).
Рассмотрим класс UserProvider (/Global/Auth/UserProvider.cs):
SXEOLF FODVV 8VHU3URYLGHU ,3ULQFLSDO
^
SULYDWH8VHU,QGHQWLW\ XVHU,GHQWLW\ ^JHW VHW`
UHJLRQ ,3ULQFLSDO 0HPEHUV
SXEOLF,,GHQWLW\ ,GHQWLW\
^
JHW
^
UHWXUQXVHU,GHQWLW\
`
`
SXEOLF ERRO ,V,Q5ROH VWULQJUROH
^
LIXVHU,GHQWLW\ 8VHU QXOO
^
UHWXUQ IDOVH
`
UHWXUQXVHU,GHQWLW\ 8VHU ,Q5ROHV UROH
`
HQGUHJLRQ
SXEOLF 8VHU3URYLGHU VWULQJQDPH ,5HSRVLWRU\ UHSRVLWRU\
^
XVHU,GHQWLW\ QHZ8VHU,QGHQWLW\
XVHU,GHQWLW\ ,QLW QDPH UHSRVLWRU\
`
SXEOLF RYHUULGH VWULQJ 7R6WULQJ
^
UHWXUQXVHU,GHQWLW\ 1DPH
`
Наш 8VHU3URYLGHU знает про то, что его ,,GHQWLW\ классом есть8VHU,GHQWLW\, а поэтому знает про класс 8VHU, внутри которого мы реализуем метод ,Q5ROHV UROH:
SXEOLF ERRO ,Q5ROHV VWULQJUROHV
^
LI VWULQJ,V1XOO2U:KLWH6SDFH UROHV
^
UHWXUQ IDOVH
`
YDUUROHV$UUD\ UROHV 6SOLWQHZ>@ ^ ` 6WULQJ6SOLW2SWLRQV 5HPRYH(PSW\(QWULHV
IRUHDFK YDUUROHLQUROHV$UUD\
^
YDUKDV5ROH 8VHU5ROHV$Q\ S !VWULQJ&RPSDUH S 5ROH &RGH UROHWUXH
LIKDV5ROH
^
UHWXUQ WUXH
`
`
UHWXUQ IDOVH
`
В метод ,Q5ROHV мы ожидаем, что придет запрос о ролях, которые допущены к ресурсу, разделенные запятой. Т.е., например, “admin,moderator,editor”, если хотя бы одна из ролей есть у нашего 8VHU – то возвращаем зачение «истина» (доступ есть). Сравниваем по полю Role.Code,
а не Role.Name.
Рассмотрим класс UserIdentity (/Global/Auth/UserIdentity.cs):
SXEOLF FODVV 8VHU,QGHQWLW\ ,,GHQWLW\
^
SXEOLF8VHU 8VHU ^JHW VHW`
SXEOLF VWULQJ $XWKHQWLFDWLRQ7\SH
^
JHW
^
UHWXUQ W\SHRI8VHU 7R6WULQJ
`
`
SXEOLF ERRO,V$XWKHQWLFDWHG
^
JHW
^
UHWXUQ8VHU QXOO
`
`
SXEOLF VWULQJ1DPH
^
JHW
^
LI8VHU QXOO
^
UHWXUQ8VHU (PDLO
`
ɢɧɚɱɟ ɚɧɨɧɢɦ
UHWXUQ DQRQ\P
`
`
SXEOLF YRLG ,QLW VWULQJHPDLO ,5HSRVLWRU\ UHSRVLWRU\
^
LI VWULQJ,V1XOO2U(PSW\ HPDLO
^
8VHU UHSRVLWRU\ *HW8VHU HPDLO
`
`
`
В ,5HSRVLWRU\ добавляем новый метод *HW8VHU HPDLO. Реализация для6TO5HSRVLWRU\ *HW8VHU (LessonProject.Model:/SqlRepository/User.cs):
SXEOLF8VHU*HW8VHU VWULQJHPDLO
^
UHWXUQ'E 8VHUV )LUVW2U'HIDXOW S !VWULQJ&RPSDUH S (PDLO HPDLOWUXH
`
Почти все готово. Выведем CurrentUser в BaseController:
>,QMHFW@
SXEOLF,$XWKHQWLFDWLRQ$XWK ^JHW VHW`
SXEOLF8VHU &XUUHQW8VHU
^
JHW
^
UHWXUQ8VHU,QGHQWLW\$XWK &XUUHQW8VHU ,GHQWLW\ 8VHU
`
`
Да, это не очень правильно, так как здесь присутствует сильное связывание. Поэтому сделаем так, введем еще один интерфейс ,8VHU3URYLGHU, из которого мы будем требовать вернуть нам авторизованного 8VHU:
SXEOLF LQWHUIDFH ,8VHU3URYLGHU
^
8VHU 8VHU ^JHW VHW`
`
«
SXEOLF FODVV 8VHU,QGHQWLW\ ,,GHQWLW\ ,8VHU3URYLGHU
^
VXPPDU\!
Ɍɟɤɳɢɣ ɩɨɥɶɡɨɜɚɬɟɥɶ
VXPPDU\!
SXEOLF8VHU 8VHU ^JHW VHW`
«
>,QMHFW@ SXEOLF,$XWKHQWLFDWLRQ$XWK ^JHW VHW`
SXEOLF8VHU &XUUHQW8VHU
^
JHW
^
UHWXUQ,8VHU3URYLGHU$XWK &XUUHQW8VHU ,GHQWLW\ 8VHU
`
`
А теперь попробуем инициализировать это всё.
Вначале добавим наш IAuthentication + CustomAuthentication в регистрацию к Ninject (/App_Start/NinjectWebCommon.cs):
NHUQHO %LQG ,$XWKHQWLFDWLRQ! 7R &XVWRP$XWKHQWLFDWLRQ! ,Q5HTXHVW6FRSH
Потом создадим модуль, который будет на событие AuthenticateRequest совершать действие авторизации:
SXEOLF FODVV $XWK+WWS0RGXOH ,+WWS0RGXOH
^
SXEOLF YRLG ,QLW+WWS$SSOLFDWLRQ FRQWH[W
^
FRQWH[W$XWKHQWLFDWH5HTXHVW QHZ(YHQW+DQGOHUWKLV $XWKHQWLFDWH
`
SULYDWH YRLG $XWKHQWLFDWH2EMHFW VRXUFH (YHQW$UJV H
^
+WWS$SSOLFDWLRQ DSS +WWS$SSOLFDWLRQ VRXUFH
+WWS&RQWH[W FRQWH[W DSS &RQWH[W
YDUDXWK 'HSHQGHQF\5HVROYHU &XUUHQW *HW6HUYLFH ,$XWKHQWLFDWLRQ!
DXWK +WWS&RQWH[W FRQWH[W
FRQWH[W 8VHU DXWK &XUUHQW8VHU
`
SXEOLF YRLG 'LVSRVH
^
`
`