|
1 | 1 | ## Аутентификация <br>
|
2 | 2 | Думаю, что во многих приложениях, особенно в банковских, вы встречались со следующим поведением приложения: работаете, работаете, а потом вдруг появляется экран ввода пин-кода. Такое поведение обосновано мерами безопасности. Опишем данный процесс чуть более подробно с некоторыми техническими подробностями. <br>
|
3 |
| -При старте приложения вам показывается экран с вводом пинкода. Вы посылаете запрос на сервер, и в случае успеха (верного пинкода) вам возвращается токен, и стартует сессия. Далее часть запросов вы шлете с использованием данного токена. Токен прикрепляется либо к заголовку запроса, либо к телу. Подобные запросы еще также называются запросами авторизованной зоны. То есть сессия - это время, в течении которого данный токен действителен на стороне сервера.<br> |
| 3 | +При старте приложения вам показывается экран с вводом пин-кода. Вы посылаете запрос на сервер, и в случае успеха (верного пин-кода) вам возвращается токен, и стартует сессия. Далее часть запросов вы шлете с использованием данного токена. Токен прикрепляется либо к заголовку запроса, либо к телу. Подобные запросы еще также называются запросами авторизованной зоны. То есть сессия - это время, в течении которого данный токен действителен на стороне сервера.<br> |
4 | 4 | Через какое-то время сессия завершается, обычно по истечению какого-то конкретного промежутка времени, и тогда токен "протухает". Запросы с протухшим токеном возвращают ошибку. Тогда либо нужно просто обновить токен специальным запросом, либо нужно пользователя снова перебросить на экран ввода пин-кода, обновить токен, и продолжить слать запросы авторизованной зоны с обновившимся токеном. <br>
|
5 | 5 | А теперь представим, что подобное поведение вам необходимо реализовать в своем приложении. И архитектура вашего приложения - Чистая архитектура. <br>
|
6 | 6 | Поэтому возникает ряд интересных вопросов: <br>
|
@@ -85,7 +85,7 @@ public class MainAuthenticator implements Authenticator {
|
85 | 85 | }
|
86 | 86 | ```
|
87 | 87 | Благодаря добавлению ```synchronized``` к методу ```authenticate``` и блокирующему методу ```authHolder.refresh()``` мы добиваемся того, что при возникновении 401 ошибки мы первым делом идем обновлять токен. Все остальные запросы, которые возвращают 401 ошибку, тоже ждут обновления токена, и только после этого идут повторные запросы с обновленным токеном. Очень удобно!<br>
|
88 |
| -Логичный вопрос. А как должен обновляться **AuthHolder**, через что он организизует свой запрос обновления токена. Так как опять-таки это внутренняя кухня организации работы с сервером, то Repositories и другие слои про это не знают. Это внутреннее дело **Data**. <br> |
| 88 | +Логичный вопрос. А как должен обновляться **AuthHolder**, через что он организует свой запрос обновления токена. Так как опять-таки это внутренняя кухня организации работы с сервером, то Repositories и другие слои про это не знают. Это внутреннее дело **Data**. <br> |
89 | 89 | Хорошо, а через что тогда лучше осуществлять запрос? Через **CommonNetwork**, который будет включать классы для осуществления запросов неавторизованной зоны (запросов, не требующих токена). В [примере](https://github.com/AndroidArchitecture/AuthCase/tree/sample_1) эти классы объединены в **CommonNetworkModule**.<br>
|
90 | 90 | Обновленная схема приобретет вид:<br>
|
91 | 91 | 
|
@@ -280,7 +280,7 @@ public class AuthHolder {
|
280 | 280 | }
|
281 | 281 | ```
|
282 | 282 | Обратим внимание на метод ```public void refresh()```. В нем мы обнуляем пинкод, сообщаем подписчику об истечении сессии, в данном случае сообщаем **AuthRepository**. Затем через обычный ```CountDownLatch``` мы блокируем поток до тех пор пока не будет вызван метод ```public void updatePinCode(@NonNull String pinCode)```. В данном методе приходит введенный пользователем пин-код (**синие стрелочки** на рисунке выше). <br>
|
283 |
| -На всякий случай отмечу следующий момент. В данном случае обновление пин-кода - синхронная операция и выполняется она в главном потоке, поэтому с главного потока мы без проблем освобождаем поток, задержанный ```CountDownLatch```. Но если обновление пин-кода была бы асинхронной операцией, и выполнялась бы в другом потоке, то поток этот должен был браться не из ```Executors``` в **AuthNetwork**, иначе теоретически могло получиться, что все потоки были бы в режиме ожидания, и обновить пин-код было бы некому.<br> |
| 283 | +На всякий случай отмечу следующий момент. В данном случае обновление пин-кода - синхронная операция и выполняется она в главном потоке, поэтому с главного потока мы без проблем освобождаем поток, задержанный ```CountDownLatch```. Но если обновление пин-кода была бы асинхронной операцией, и выполнялась бы в другом потоке, то поток этот должен был браться не из ```Executors``` в **AuthNetwork**, иначе теоретически могло получиться, что все потоки были бы в режиме ожидания, и обновить пин-код было бы некому.<br> |
284 | 284 | После того как пришел пин-код, поток, вызвавший ```refresh```, разблокируется и запускает запрос по обновлению токена (метод ```private Single<String> updateToken()```). В случае успешного обновления, остальные запросы с 401 ошибкой также перестартуются.<br>
|
285 | 285 | Конечно же, механизм по обновлению токена с ожиданиями через ```CountDownLatch``` вы можете переделать по своему усмотрению. В этом решении также могут быть не учтены некоторые крайние случаи. Тут важна опять-таки - концепция.<br>
|
286 | 286 | Вот собственно и все манипуляции =)
|
0 commit comments