Донецкий техникум промышленной автоматики

Принудительный вход пользователя в свою учетную запись Google Organization (G Suite)

  1. Включение входа в Google
  2. Тестирование это
  3. Ограничение пользователей вашим организационным доменом
  4. Указание параметра размещенного домена
  5. Код

Опубликовано: 12 сентября 2017 г.

У Microsoft есть хороший документ, объясняющий, как разрешить пользователям войдите в приложение с помощью своих аккаунтов Google , Мне было любопытно посмотреть, как можно заставить пользователей войти в систему с помощью учетной записи организации.

Зачем тебе это делать?

Ну, скажем, ваша компания использует G Suite в качестве службы каталогов. Разрешение только пользователям входить со своим организационным адресом электронной почты (G Suite) означает, что вам нужно только закупать пользователей в службе каталогов G Suite. Вам не нужно беспокоиться о поддержке отдельного хранилища пользователей для управления пользователями или о том, что люди из вашей организации могут войти в систему.

В моем случае у меня есть домен jerriepelser.com, который управляется G Suite. В этом примере я покажу вам, как настроить мое приложение, чтобы разрешить только пользователям с адресом электронной почты jerriepelser.com входить в приложение.

Мы начнем с нового проекта MVC, поэтому давайте создадим новое приложение с помощью инструмента командной строки .NET Core:

Включение входа в Google

Первое, что вам нужно сделать, это создать приложение в консоли API Google. Вы можете обратиться к Документация Microsoft о том, как это сделать.

После того как вы зарегистрировали приложение в консоли API Google, добавьте свой идентификатор клиента и секрет клиента в свой файл appsettings.json:

{"Аутентификация": {"Google": {"ClientId": "...", "ClientSecret": "..."}}}

Зарегистрируйте службу аутентификации в методе ConfigureServices вашего класса запуска:

// Startup.cs public void ConfigureServices (IServiceCollection services) {services.AddMvc (); services.AddAuthentication (options => {options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;}) .AddCookie (). Google: ClientId "]; options.ClientSecret = Конфигурация [" Аутентификация: Google: ClientSecret "];}); }

Обратите внимание, что я установил DefaultAuthenticateScheme и DefaultSignInScheme на куки. Это означает, что весь запрос будет аутентифицирован с использованием файлов cookie, и как только внешний поставщик (например, в данном случае Google) подтвердит подлинность пользователя, пользователь войдет в приложение с помощью файла cookie.

В звонке в AddGoogle я указал схему аутентификации как Google . Если вы не укажете параметр authenticationScheme, на самом деле он также будет по умолчанию Google , но я хочу быть откровенным об этом, чтобы вы могли понять, как провайдер аутентификации Google получает вызов (или срабатывает) в действии Вход в систему ниже.

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

// Startup.cs public void Configure (приложение IApplicationBuilder, IHostingEnvironment env) {if (env.IsDevelopment ()) {app.UseDeveloperExceptionPage (); } else {app.UseExceptionHandler ("/ Home / Error"); } app.UseStaticFiles (); app.UseAuthentication (); app.UseMvc (rout => {rout.MapRoute (имя: "default", шаблон: "{controller = Home} / {action = Index} / {id?}");}); }

Затем я создаю класс AccountController с действиями Login, Logout и Profile.

открытый класс AccountController: Controller {public Async Task Login (строка returnUrl = "/") {await HttpContext.ChallengeAsync ("Google", new AuthenticationProperties () {RedirectUri = returnUrl}); } [Authorize] public async Task Logout () {await HttpContext.SignOutAsync (CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties {RedirectUri = Url.Action ("Index", "Home")}); } [Authorize] public IActionResult Profile () {return View (); }}

Действие Вход в систему вызывает у поставщика аутентификации схему Google . Таким образом, он вызовет обработчик аутентификации Google, который мы указали ранее, потому что мы зарегистрировали его в Scheme для аутентификации Google . После успешного входа он перенаправит пользователя к значению, переданному в параметре returnUrl, который по умолчанию соответствует домашней странице.

Действие Выход из системы просто выведет пользователя из обработчика cookie, очистив cookie. После выхода он перенаправит пользователя на домашнюю страницу.

Действие профиля отобразит представление только со списком претензий для пользователя. Ничего особенного, но это позволяет нам видеть, какие претензии возвращаются Google.

В папке / Views / Account создайте новый файл представления с именем Profile.cshtml. Вот код для представления:

@ {ViewData ["Title"] = "Профиль"; } <div class = "row"> <div class = "col-md-12"> <h3> Заявки, связанные с текущим пользователем </ h3> <table class = "table"> <thead> <tr> <th> Утверждение </ th> <th> Значение </ th> </ tr> </ thead> <tbody> @foreach (утверждение var в User.Claims) {<tr> <td> @ demand.Type </ td> < td> @ demand.Value </ td> </ tr>} </ tbody> </ table> </ div> </ div>

Наконец, нам нужно добавить кнопки входа и выхода в меню. Перейдите в файл /Views/Shared/_layout.cshtml и в разделе панели навигации добавьте несколько ссылок на действия входа и выхода из системы. В случае, когда пользователь проходит проверку подлинности, мы также отобразим его имя, которое связано с действием профиля.

<! - какой-то код пропущен -> <nav class = "navbar navbar-inverse navbar-fixed-top"> <div class = "container"> <div class = "navbar-header"> <button type = "button "class =" navbar-toggle "data-toggle =" collapse "data-target =" .navbar-collapse "> <span class =" sr-only "> Переключить навигацию </ span> <span class =" icon-bar "> </ span> <span class =" icon-bar "> </ span> <span class =" icon-bar "> </ span> </ button> <a asp-area =" "asp-controller = "Home" asp-action = "Index" class = "navbar-brand"> GoogleAuth </ a> </ div> <div class = "navbar-collapse collapse"> <ul class = "nav navbar-nav"> < li> <ap-area = "" asp-controller = "Home" asp-action = "Индекс"> Home </ a> </ li> <li> <a asp-area = "" asp-controller = " Home "asp-action =" О программе "> О программе </ a> </ li> <li> <a asp-area =" "asp-controller =" Home "asp-action =" Контакт "> Контакт </ a> </ li> </ ul> <ul class = "nav navbar-nav navbar-right"> @if (User.Identity.IsAuthenticated) {<li> <a asp-controller = "Аккаунт" asp-action = "Профиль"> Hello @ User.Identity.Name! </ A> </ li> <li> <ap-controller = "Аккаунт" asp-action = "Выйти"> Выйти </ a> </ li>} else {<li> <a asp-controller = "Account" asp-action = "Login"> Login </ a> </ li>} </ ul> </ div> </ div > </ nav> <! - какой-то код опущен ->

Тестирование это

Отлично, давайте проверим это, запустив приложение. Нажмите на ссылку для входа в правом верхнем углу:

Это перенаправляет меня в Google для аутентификации. Вы можете видеть, что я вошел в Google с обоими учетными записями электронной почты jerriepelser.com и Auth0. Итак, давайте перейдем на учетную запись jerriepelser.com:

Это перенаправляет меня обратно к моему заявлению, и вы можете видеть, что я вошел в систему:

Если я нажму на Привет Джерри Пелсер! текст Я попаду на страницу профиля, где вы сможете увидеть все претензии, которые были возвращены Google.

Ограничение пользователей вашим организационным доменом

Прямо сейчас я также могу войти в свою учетную запись Google Auth0 или любую другую учетную запись Google, которая у меня есть. Мы хотим ограничить это только пользователями в домене jerriepelser.com, но прежде чем мы это сделаем, давайте быстро поймем, что делает обработчик аутентификации Google.

Как только пользователь прошел аутентификацию в Google, обработчик аутентификации Google собирается вызвать конечную точку с информацией о пользователе по адресу https://www.googleapis.com/plus/v1/people/me.

Эта конечная точка будет возвращать объект JSON, подобный следующему:

{"kind": "plus # person", "etag": "\" Sh4n9u6EtD24TM0RmWv7jTXojqc / LyG9i73o_qqjFbpRIt1nc9uVTvk \ "", "emails": [{"value": "... @ jerriepelser.com", ", account "}]," objectType ":" person "," id ":" 1081425 .... "," displayName ":" Jerrie Pelser "," name ": {" familyName ":" Pelser "," GivenName " : "Jerrie"}, "image": {"url": "https://lh5.googleusercontent.com/-UEA_Kl3MzOE/AAAAAAAAAAI/AAAAAAAAAMQ/WDoAqIJGYTU/photo.jpg?sz=50", "isDefault": false , "isPlusUser": false, "language": "en", "ageRange": {"min": 21}, "Verified": false, "domain": "jerriepelser.com"}

Затем обработчик аутентификации Google собирается вызвать событие OnCreatingTicket, передавая экземпляр OAuthCreatingTicketContext. Этот контекст будет содержать свойство User, которое позволяет нам получить доступ к полезной нагрузке JSON, возвращаемой из Google, которую я показал в примере выше.

Если вы заинтересованы в коде, который делает все это, вы можете проверить его здесь

Итак, теперь давайте обработаем это событие OnCreatingTicket.

Вернитесь к методу ConfigureServices и давайте изменим регистрацию обработчика аутентификации Google, добавив обработчик события OnCreatingTicket. Внутри этого обработчика событий нам нужно проверить свойство домена Пользователя, чтобы узнать, является ли оно на самом деле jerriepelser.com . Если это так, то мы счастливы. Если это не так, мы бросаем исключение:

public void ConfigureServices (IServiceCollection services) {services.AddMvc (); services.AddAuthentication (options => {options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;}) .AddCookie (). Google: ClientId "]; options.ClientSecret = Конфигурация [" Аутентификация: Google: ClientSecret "]; options.Events = new OAuthEvents {OnCreatingTicket = context => {строковый домен = context.User.Value <string> (" домен ") ; if (domain! = "jerriepelser.com") выдает новое исключение GoogleAuthenticationException ("Вы должны войти в систему с помощью адреса электронной почты jerriepelser.com"); return Task.CompletedTask;}};}); }

Этот класс GoogleAuthenticationException является просто классом, который наследуется от Exception. Ничего особенного. Однако причина, по которой я использую пользовательский класс исключений, заключается в том, что я могу отобразить более приятное сообщение об ошибке для пользователя.

Наш созданный шаблон приложения имеет обработчик ошибок, который можно найти в классе HomeController. Это просто отображает общее сообщение об ошибке.

Однако в таком случае я хочу отобразить фактическое сообщение об ошибке («Вы должны войти с адресом электронной почты jerriepelser.com») пользователю. Итак, давайте сначала изменим класс ErrorViewModel (расположенный в /ViewModels/ErrorViewModel.cs), чтобы также добавить свойство ErrorMessage.

открытый класс ErrorViewModel {открытая строка ErrorMessage {get; задавать ; } публичная строка RequestId {get; задавать ; } public bool ShowRequestId =>! строка .IsNullOrEmpty (RequestId); }

Затем мы изменим действие Error, чтобы получить исключение, и попытаемся привести его к исключению GoogleAuthenticationException. Если это исключение GoogleAuthenticationException, мы добавим сообщение в свойство ErrorMessage объекта ErrorViewModel. В качестве альтернативы, свойство ErrorMessage будет нулевым.

открытый класс HomeController: Controller {// некоторый код опущен для краткости ... public IActionResult Error () {var exceptionHandlerFeature = HttpContext.Features.Get <IExceptionHandlerFeature> (); var exception = exceptionHandlerFeature? .Error as GoogleAuthenticationException; возвратный просмотр (новый ErrorViewModel {RequestId = Activity.Current? .Id ?? HttpContext.TraceIdentifier, ErrorMessage = исключение? .Message}); }}

Давайте также изменим /Views/Shared/Error.cshtml для отображения ErrorMessage, если оно есть. Если нет, то будет отображаться общее сообщение «Произошла ошибка…»:

@model ErrorViewModel @ {ViewData ["Title"] = "Ошибка"; } <h1 class = "text-danger"> Ошибка. </ h1> @if (! string.IsNullOrEmpty (Model.ErrorMessage)) {<h2 class = "text-danger"> @ Model.ErrorMessage </ h2>} else {<h2 class = "text-danger"> Произошла ошибка при обработке вашего запроса. </ h2>} @if (Model.ShowRequestId) {<p> <strong> Идентификатор запроса: </ strong> <code> @ Model.RequestId </ code> </ p>} <h3> Режим разработки </ h3> <p> Переход в среду <strong> Разработка </ strong> отобразит более подробную информацию о возникшей ошибке. </ p> <p> <strong> Среду разработки не следует включать в развернутых приложениях </ strong>, так как это может привести к тому, что конфиденциальная информация из исключений будет отображаться для конечных пользователей. Для локальной отладки среду разработки можно включить, установив для переменной <strong> ASPNETCORE_ENVIRONMENT </ strong> значение <strong> Разработка </ strong> и перезапустив приложение. </ p>

Наконец-то мы снова можем его раскрутить. На этот раз, если я войду в свой почтовый ящик jerriepelser.com, все будет работать как раньше. Но если я пытаюсь войти в систему с моей учетной записью Auth0, я получаю следующую ошибку:

Указание параметра размещенного домена

Есть еще одна вещь, которую мы можем сделать, это указать параметр Hosted Domain или hd, когда пользователь перенаправляется на конечную точку авторизации Google (см. документы ). Вот соответствующий раздел из этих документов:

Включив домен пользователя G Suite (например, mycollege.edu), вы можете указать, что пользовательский интерфейс выбора учетной записи должен быть оптимизирован для учетных записей в этом домене.

Таким образом, это будет в основном предварительно заполнять домен, как и все, что мы передаем (в нашем случае jerriepelser.com), и пользователю нужно будет заполнить только часть имени адреса электронной почты.

Мы снова вернемся к методу ConfigureServices и на этот раз добавим событие OnRedirectToAuthorizationEndpoint. Это просто добавит этот параметр hd в конец RedirectUri, а затем перенаправит пользователя на эту конечную точку:

public void ConfigureServices (IServiceCollection services) {services.AddMvc (); services.AddAuthentication (options => {options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;}) .AddCookie (). Google: ClientId "]; options.ClientSecret = Конфигурация [" Аутентификация: Google: ClientSecret "]; options.Events = new OAuthEvents {OnRedirectToAuthorizationEndpoint = context => {context.Response.Redirect (context.RedirectUri +" & hd = "+ система .Net.WebUtility.UrlEncode ("jerriepelser.com")); вернуть Task.CompletedTask;}, OnCreatingTicket = context => {string domain = context.User.Value <string> ("domain"); if (domain! =) «jerriepelser.com») генерирует новое исключение GoogleAuthenticationException («Вы должны войти в систему с помощью адреса электронной почты jerriepelser.com»); return Task.CompletedTask;}};}); }

Давайте попробуем это снова.

Что происходит со мной, когда я делаю это, это то, что приглашение для входа в Google даже не отображается. Из приложения я перенаправляюсь в Google, и, поскольку я уже вошел в систему с пользователем с этим доменом, Google немедленно возвращает его как аутентифицированного пользователя в ваше приложение.

Чтобы лучше понять, что происходит, вам сначала нужно выйти из своей учетной записи Google или открыть окно браузера Incognito:

Обратите внимание на скриншот выше, что доменная часть предварительно заполнена как jerriepelser.com.

Вы можете спросить, почему нам все еще нужна проверка в событии OnCreatingTicket. Что ж, в документации Google прямо говорится, что мы не должны полагаться на эту оптимизацию пользовательского интерфейса, чтобы контролировать, кто может получить доступ к нашему приложению :

Я не уверен на 100%, но я подозреваю, что параметр состояния, сгенерированный средством аутентификации Google, проверит и предотвратит вмешательство кого-либо в исходящий запрос. Однако, в любом случае, просто сохраните этот код на месте, чтобы быть в безопасности.

Код

Исходный код можно найти по адресу jerriepelser-блог / Google-gsuite-OAuth ,

Если вы нашли ценность в этом сообщении в блоге и хотите вернуть услугу, вы можете Купи мне кофе

PS: я публикую еженедельную рассылку для разработчиков ASP.NET под названием ASP.NET Weekly . Если вы хотите получать электронную почту каждую пятницу со всеми лучшими сообщениями в блогах, связанных с ASP.NET за предыдущую неделю, пожалуйста, Подписаться !

Похожие

Тайна убийства Google Планета Земля разоблачена
... Google Планета Земля, которая сделала штурм в Интернете. На изображении (выше), размещенном 12 апреля, изображено, как выглядит пара людей, тащащих тело по пропитанному кровью пирсу. Надпись гласила: «Убийство рядом с моим домом на Google Maps, ссылка в комментариях». Ссылка, на которую ссылались, помещала изображение в Алмере, Нидерланды, и ncav сказал, что он уведомил полицию. Все это делает интересную историю. Кто не любит загадки и интриги, особенно если преступные действия пошли
Зачем мне магазин, если это Аллегро?
Вчера появилась информация, что Amazon готовится открыть сеть пунктов сбора в Великобритании. Представители крупнейшего в мире интернет-магазина быстро опровергли эти сообщения . В газете
Система отслеживания ошибок Google содержала свои уязвимости
Исследователь обнаружил дыры в безопасности в базе данных Google для отслеживания ошибок, которые могли потенциально привести к тому, что злоумышленники получили доступ к конфиденциальной информации, включая сведения о способах использования незащищенных уязвимостей в продуктах Google. Исследователь Алекс Бирсан имеет описано как ему удалось обмануть систему отслеживания
AdWords - создание учетной записи Google AdWords ⋆ Project-Net Internet Technologies
Любой, кто решит использовать интернет-рекламу в поиске Google, получит эффективный инструмент для привлечения многих потенциальных клиентов. Однако, прежде чем попасть туда, вам придется сделать несколько шагов. Первым из них, конечно же, является создание учетной записи AdWords, для
Обновление Касперского обеспечивает доступ в Интернет для пользователей Windows XP
... ватели Windows XP, которые запускают определенное антивирусное программное обеспечение Kaspersky, могут оказаться в автономном режиме после загрузки нового обновления. Вышедшее вчера обновление приводит к тому, что компьютеры под управлением Windows XP теряют подключение к Интернету. ИТ-администраторы, которые используют Kaspersky Endpoint Security в своих организациях вмешались в форум Касперского вчера и сегодня
Как добавить Google Analytics в ваш блог WordPress
... Google Analytics на свой веб-сайт, вы можете анализировать свои данные, чтобы увидеть, что работает, а что нет. Вот несколько примеров типов данных, которые вы можете увидеть: Данные в реальном времени (кто сейчас посещает ваш сайт в режиме реального времени) Почасовая и ежедневная активность Демография (по городам, штатам, странам, континентам) Новое против возвращения Мобильный пользователь против пользователей рабочего стола
Совместное использование Google Voice и Gizmo Project
Смотрите обновление ниже (пропустить, чтобы обновить) Гугл голос Google-воплощение GrandCentral - фантастический сервис, который призван стать коммутатором вашего виртуального телефона. Он дает вам бесплатный номер телефона, который может принимать обычные телефонные звонки и перенаправлять их на любые другие реальные телефоны, которые вы подключили к своей учетной записи. Мощные вещи. К сожалению, его
Обзор Doogee X5 Max Pro - самый дешевый смартфон с 2 ГБ оперативной памяти
Doogee - бренд смартфонов для просмотра в 2017 году Вы, вероятно, никогда не слышали о Doogee раньше, но они делают волны в секторе бюджетных смартфонов с их линейкой мобильных телефонов X5. Последний из этих бюджетных телефонов - Doogee X5 Max Pro. Версия Max Pro отличается от других телефонов Doogee X5 улучшенными техническими характеристиками, не требуя дополнительных
Это лучшие антивирусные сканеры для Mac
Вирусные сканеры: это лучшие Антивирусный сканер предлагает дополнительную защиту для вас макинтош против вирусов, троянов и других вредоносных программ. Если вы ищете антивирусный пакет для пользователя Mac, вы можете выбрать из 15 или около того провайдеров. Какой самый лучший? Вы можете прочитать это в этой статье. В конце мы также обсудим, нужен ли вам такой антивирус на Mac.
Как вставить галочку или крестик в Microsoft Word и Excel
... в вставить символ галочки (также известный как галочка или галочка) в Microsoft Word, методы, которые мы описываем ниже, относятся к Microsoft Office 365, Microsoft Word 2016, Microsoft Word 2013, Microsoft Word 2011, Microsoft Word 2010 , Microsoft Word 2007, Microsoft Word 2008 и Microsoft Word 2004 Чтобы скопировать и вставить галочку или крест, выделите один из галочек или крестиков ниже, а затем скопируйте и вставьте его в пункт назначения. Выделите
Бесплатная и анонимная альтернатива Chatroulette
Случайный анонимный чат без регистрации На xcusy вы можете начать без регистрации. Просто выберите свой пол и возраст, и через секунду вы будете на связи с мальчиком или девочкой со всего мира. Как и в Chatroulette, вы случайно подключаетесь к человеку. Если вам не нравится ваш собеседник, вы можете легко перейти к следующему. Если вы не одиноки, вы также можете выбрать, что ваш

Комментарии

Во-вторых, если вы используете что-то из Википедии в качестве источника, как вы это цитируете?
Во-вторых, если вы используете что-то из Википедии в качестве источника, как вы это цитируете? Стоит ли использовать Википедию при написании статьи? Как правило, вы не должны использовать Википедию в качестве источника для академического письма. Поскольку любой может внести свой вклад в статью, нет никакой гарантии, что информация будет точной, актуальной или оригинальной. В некоторых университетах действуют строгие правила использования Википедии. Вот
Но почему это помогает?
Но почему это помогает? Что делает «Force stop» и что такое кеш? Позволь мне объяснить. Force Stop В основе Android лежит ядро ​​Linux, компонент, отвечающий за управление памятью и процессами, а также целый ряд других ресурсов. Каждый раз, когда вы запускаете приложение, вы фактически запускаете процесс Linux. Процесс - это логический контейнер для программы (приложения). Он запускается ядром и используется для совместного использования системных ресурсов (включая
Если это у вас с ограниченным бюджетом, задумывались ли вы о подходе DIY к фотографии продуктов?
Если это у вас с ограниченным бюджетом, задумывались ли вы о подходе DIY к фотографии продуктов? Это не так сложно, как вы думаете. Есть много методов для съемки успешных фотографий продуктов, но тот, который я собираюсь показать вам, широко известен как Техника Оконного Света. Этот учебник был специально разработан для владельцев бизнеса с ограниченным бюджетом. Он разработан так, чтобы быть простым и обеспечивать превосходные, высококачественные результаты для большинства типов
Как работает А / Б тестирование?
Как работает А / Б тестирование? Теперь, когда у вас есть общее представление о том, что и почему A / B-тестирования, давайте углубимся в специфику. Клиентское тестирование включает в себя отправку одной и той же версии страницы каждому посетителю, а затем использование Javascript для внесения изменений и настройки в браузере посетителя до того, как посетителю будет показана получившаяся страница. Тестирование на стороне сервера -
Это построено с платформой, которая поддерживает космический полет?
Это построено с платформой, которая поддерживает космический полет? Вот тот, который очень близок к этому - инновационный Drupal 8 ! Его неотразимые преимущества побуждают все больше компаний переходить на Drupal 8 или даже переходить с других CMS. Преимущества
Почему это не относится к Spotify или Netflix?
Почему это не относится к Spotify или Netflix? Все приложения в App Store подчиняются этому режиму, но большинство не навязывает эту разницу в цене пользователям. Есть несколько способов обойти этот 30% налог. Снимок экрана приложения
Как это круто?
Как это круто? Я люблю их историю, я люблю их обслуживание, мне нравится, как они экономят мне деньги, и на самом деле, заботятся об этом. Не так, как банки, которые возьмут больше, чем им нужно. И они австралийцы. Так гениально. Но не волнуйтесь, если вы не австралиец. Они в сети. Вы можете использовать их откуда угодно, чтобы перевести деньги за границу куда угодно (из 55 валют). Я использую их сейчас из США, чтобы заплатить моему австралийскому
Делает ли это простой проект совместимости с AirPrint или возможность печати с вашего устройства Android этим проектом, который вы с нетерпением ждете?
Делает ли это простой проект совместимости с AirPrint или возможность печати с вашего устройства Android этим проектом, который вы с нетерпением ждете? Поделитесь своими мыслями в комментариях! Кредиты изображений: Solarbotics Через Flickr Узнайте больше о: печать , Raspberry Pi
А если это программное обеспечение, без которого мы не можем жить?
А если это программное обеспечение, без которого мы не можем жить? У вас есть адекватный ответ на эту проблему. Это устройство состоит из памяти объемом 8 ГБ . И если также эта емкость недостаточна для его пользователя, он имеет возможность присоединиться к этой SD-карте пространства для регулировки. Если это его спикер, проблем не возникает, потому что он очень хороший и отзывчивый. Разговор хорошо слышен, а тексты точны при воспроизведении видеоигр, музыки и
Мы обсуждали это раньше: у вас есть один?
Мы обсуждали это раньше: у вас есть один? антивирус на Mac ? Это полностью зависит от вашего собственного поведения. Часто говорят, что макинтош нечувствителен к вредоносным программам, но это также может привести к ложной уверенности. Хотя вредоносного ПО для него гораздо меньше macOS High Sierra тогда для Windows это не
Новый модели iPad Pro (2018) каждый из них имеет порт USB-C в первый раз, но что это значит?
Как это круто? Я люблю их историю, я люблю их обслуживание, мне нравится, как они экономят мне деньги, и на самом деле, заботятся об этом. Не так, как банки, которые возьмут больше, чем им нужно. И они австралийцы. Так гениально. Но не волнуйтесь, если вы не австралиец. Они в сети. Вы можете использовать их откуда угодно, чтобы перевести деньги за границу куда угодно (из 55 валют). Я использую их сейчас из США, чтобы заплатить моему австралийскому

Зачем тебе это делать?
Имя: "default", шаблон: "{controller = Home} / {action = Index} / {id?
Jpg?
Get <IExceptionHandlerFeature> (); var exception = exceptionHandlerFeature?
Current?
Id ?
TraceIdentifier, ErrorMessage = исключение?
Какой самый лучший?
Во-вторых, если вы используете что-то из Википедии в качестве источника, как вы это цитируете?
Стоит ли использовать Википедию при написании статьи?