Pull to refresh

Прямая запись в справочник 1C: Предприятие через Linq на примере работы с пользователями Asp.Net

Reading time6 min
Views10K

Прямая запись в справочник 1C: Предприятие через Linq на примере работы с пользователями Asp.Net


Статья описывает как зарегистрировать пользователя веб-сайта в справочнике 1С: Предприятие 8, расположенном в MSSQL базе данных. Далее пользователь может авторизоваться на сайте, используя логин и пароль, указанный при регистрации. Работа ведется только со справочником с названием Пользователи и не затрагивает систему работы с пользователями через конфигуратор 1С.

Описаны только простейшие операции по регистрации и авторизации. Вспомогательные операции по восстановлению пароля, информированию через E-mail не освещаются. Работа ведется прямым доступом к базе MSSQL через Linq. Подход позволяет использовать одновременно функционал Asp.Net и 1С, а также обойтись без посредников в виде разных CMS.



Создание справочника Пользователи в 1С: Предприятие


Предполагается, что информационная база 1С создана и расположена на MSSQL-сервере в локальной сети или удаленно у хостинг-провайдера.

Необходимо через конфигуратор создать справочник Пользователи с кодом в виде строки 9 символов и наименованием 25 символов.

Создание справочника Пользователи в 1С:Предприятие

Структура полей следующая (перечень полей взят с избытком для дальнейшего расширения функциональности):
  • Активный: Булево
  • ДатаРегистрации: Дата
  • Email: Строка 255, переменная
  • Пароль: Строка 32 переменная
  • ПарольПрефикс: Строка 16 переменная
  • Язык: Строка 255 переменная
  • ФорматДаты: Строка 20 переменная
  • КодАктивации: Строка 36 переменная
  • КодВосстановленияПароля: Строка 36 переменная
  • ДатаАктивности: Дата


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

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

Доступен список зарегистрированных пользователей сразу после определения справочника в конфигураторе

А также форма редактирования пользователя:

Интерфейс формы редактирования пользователя

Настройка прямого доступа к базе данных MSSQL



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

Начиная с версии 1.2, LinqTo1C позволяет создавать конфигурационный файл с возможностью изменения данных. Не смотря на достоинства прямой записи в базу данных, у способа есть недостатки. Например, записанная таким способом информация не будет автоматически зарегистрирована в планах обмена. Вам самостоятельно нужно заботиться о поддержании правильности данных: генерировать уникальный код, поддерживать целостность с другими объектами, проверять вновь вносимые данные.

Утилита LinqTo1C

В результате работы утилиты появятся 2 файла: dbml – для визуального представления и cs-для добавления в проект C#. Выглядит в редакторе Visual Studio это так:

Результат работы LinqTo1C в Visual Studio

Asp.Net код для регистрации и авторизации


Код приведен для Asp.Net MVC, где за работу пользователя отвечает AccountController.

AccountController:
public ActionResult LogOn()
        {
            ViewBag.Title = Resources.Account.LogonTitle;

            return View("~/Views/Dotnet/Logon.cshtml");
        }

        [HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                if (MembershipService.ValidateUser(model.UserName, model.Password))
                {
                    FormsService.SignIn(model.UserName, model.RememberMe);
                    if (Url.IsLocalUrl(returnUrl))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Dotnet");
                    }
                }
                else
                {
                    ModelState.AddModelError("", "");
                }
            }

            // If we got this far, something failed, redisplay form
            return View("~/Views/Dotnet/Logon.cshtml", model);
        }

        public ActionResult LogOff()
        {
            FormsService.SignOut();

            return RedirectToAction("Index", "Dotnet");
        }

        public ActionResult Register()
        {
            ViewBag.Title = Resources.Account.RegisterTitle;

            ViewBag.PasswordLength = MembershipService.MinPasswordLength;
            return View("~/Views/Dotnet/Register.cshtml");
        }

        [HttpPost]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                MembershipCreateStatus createStatus = MembershipService.CreateUser(model.UserName, model.Email, model.Password, model.ConfirmPassword);

                if (createStatus == MembershipCreateStatus.Success)
                {
                    FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
                    return RedirectToAction("Index", "Dotnet");
                }
                else
                {
                    ModelState.AddModelError("", AccountValidation.ErrorCodeToString(createStatus));
                }
            }

            // If we got this far, something failed, redisplay form
            ViewBag.PasswordLength = MembershipService.MinPasswordLength;
            return View("~/Views/Dotnet/Register.cshtml", model);
        }


Вызовы вида FormsService.SignOut и FormsService.SignIn не такие интересные, так как они перенаправляются к стандартным методам: FormsAuthentication.SignOut и FormsAuthentication.SetAuthCookie.

Класс AccountMembershipService осуществляет обращение к базе MSSQL.

Для регистрации пользователя предназначен метод CreateUser. Сначала проверяются возможные ошибки: пустой логин, пароль, e-mail, дублирующийся логин/e-mail, совпадение пароля и подтверждение пароля. Далее заполняются поля пользователя. Ссылке присваивается новый Guid, номер ищется как максимальный номер + 1. Пароль хранится в виде контрольной суммы в закрытом, контрольная сумма вычисляется от пароля пользователя и уникального префикса, который хранится здесь же в записи.
public MembershipCreateStatus CreateUser(string userName, string email, string password, string confirmPassword)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
            if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");

            MembershipCreateStatus status = MembershipCreateStatus.ProviderError;

            using (var dataContext = new ElisyCMS(ConfigurationManager.ConnectionStrings["ElisyCMS"].ConnectionString))
            {
                if (dataContext.СправочникПользователи.Where(m => m.Наименование == userName && m.ПометкаУдаления == new Binary(new byte[]{0})).Count() != 0)
                    return MembershipCreateStatus.DuplicateUserName;
                if (dataContext.СправочникПользователи.Where(m => m.Email == email && m.ПометкаУдаления == new Binary(new byte[] { 0 })).Count() != 0)
                    return MembershipCreateStatus.DuplicateEmail;
                if (password != confirmPassword)
                    return MembershipCreateStatus.InvalidPassword;

                try
                {
                    СправочникПользователи user = new СправочникПользователи();

                    user.Ссылка = Guid.NewGuid().ToByteArray();
                    user.ПометкаУдаления = new byte[] { 0 };
                    user.Предопределенный = new byte[] { 0 };

                    var codeRequest = from a in dataContext.СправочникПользователи
                                      where Convert.ToInt32(a.Код) > 0
                                      orderby Convert.ToInt32(a.Код) descending
                                      select Convert.ToInt32(a.Код);
                    var lastCode = codeRequest.Take(1).FirstOrDefault();
                    user.Код = (lastCode + 1).ToString().PadLeft(9, '0');

                    user.Наименование = userName;

                    byte[] saltBytes = new byte[8];
                    new RNGCryptoServiceProvider().GetBytes(saltBytes);
                    user.ПарольПрефикс = Convert.ToBase64String(saltBytes);

                    byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(user.ПарольПрефикс + password);
                    byte[] hash = new SHA1CryptoServiceProvider().ComputeHash(passwordBytes);
                    user.Пароль = Convert.ToBase64String(hash);

                    user.ДатаРегистрации = DateTime.Now;
                    user.ДатаАктивности = DateTime.Now;
                    user.Активный = new Binary(new byte[] { 1 });
                    user.Язык = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
                    //user.КодАктивации = Guid.NewGuid().ToString();

                    user.Email = email;

                    dataContext.СправочникПользователи.InsertOnSubmit(user);
                    dataContext.SubmitChanges();

                    return MembershipCreateStatus.Success;
                }
                catch (Exception ex)
                {
                    return MembershipCreateStatus.ProviderError;
                }
            }

            return status;
        }
          

Для авторизации предназначен метод ValidateUser, который после проверки параметров проверяет наличие пользователя в базе, флаг активности и пытается сравнить контрольную сумму пароля с вычисленной контрольной суммой переданного пароля и уникального префикса.
        public bool ValidateUser(string userName, string password)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");

            using (var dataContext = new ElisyCMS(ConfigurationManager.ConnectionStrings["ElisyCMS"].ConnectionString))
            {
                var пользователь = dataContext.СправочникПользователи.Where(m => m.Наименование.ToUpper() == userName.ToUpper()).FirstOrDefault();
                if (пользователь == null)
                    return false;
                if (пользователь.Активный.ToArray()[0] == 0)
                    return false;
                if (String.IsNullOrWhiteSpace(пользователь.Пароль))
                    return true;
                byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(пользователь.ПарольПрефикс + password);
                byte[] hash = new SHA1CryptoServiceProvider().ComputeHash(passwordBytes);
                return Convert.ToBase64String(hash).Equals(пользователь.Пароль);
            }
        }
Tags:
Hubs:
+5
Comments19

Articles