Интеграция ASP.NET MVC c Sharepoint 2013. Part 1: High-Trusted provider-hosted APP

    Всем привет!

    Меня зовут Денис и я работаю старшим разработчиком в компании «ДоксВижн». Этим постом я хочу начать цикл статей, касающихся разработки в области SharePoint 2013. Они затронут разные аспекты этой темы, начиная с возможностей базовой интеграции Web-приложения и заканчивая публикацией в магазине SharePoint Store. Знание нюансов и подводных камней, с которыми я столкнулся, может пригодиться тем, перед кем впервые будет стоять аналогичная задача.


    Итак, следуя хронологии, начну с описания того, как я решил первую поставленную передо мной задачу: на базе существующего ASP.NET MVC приложения “Легкий клиент Docsvision” создать новое — “Docsvision клиент для SharePoint”, который бы интегрировался в SharePoint 2013 и расширял его возможностями Легкого клиента.

    В SharePoint 2013 появилась новая модель приложений Apps, которая кардинально отличается от старой модели Solutions. Однако, пугаться не стоит — Solutions никуда не исчезли. Прежде всего, следует выбрать, что использовать: Solutions или App. В этом вопросе может помочь статья на MSDN.
    Наш выбор пал на Sharepoint App, т.к. требования к интеграции на тот момент вполне можно было реализовать через App. В дальнейшем, когда нами было принято решение об интеграции в магазин Sharepoint, оказалось, что оно совершенно верное, т.к., как вы увидите в следующих статьях, в магазине Sharepoint нельзя размещать Solutions.
    Несмотря на все это, никто не запрещает использовать оба подхода. В следующих статьях я обязательно расскажу, как нам удалось соединить App и Solutions.

    Остановимся пока на нашем выборе — SharePoint App. Согласно документации бывает несколько вариантов интеграции: SharePoint-hosted, Auto-hosted, Provider-hosted. Учитывая, что наше приложение являетcя не Web API, а MVC в классическом варианте и то, что приложение будет поставляться в режиме on-premises — мы решили использовать вариант High-Trusted Provider-Hosted App. High-Trusted подразумевает, что наше Web-приложение связывается с SharePoint через сертификат связью один к одному. Есть еще так называемый режим Low-Trusted — он используется для SharePoint Store или в локальном корпоративном каталоге приложений компании.

    Итак, мы все решили, установили полигон с SharePoint 2013 — и тут нас встречает первая неожиданность от Microsoft — нельзя так просто взять и создать App и внедрить его в SharePoint. Сначала мы должны подготовить SharePoint к использованию App. Процесс подготовки подробно описан на MSDN. Я кратко пробегусь по пунктам:
    1. проверить, что запущены сервисы SharePoint: User Profile Service Application, App Management Service
    2. проверить наличие хотя бы одного профиля пользователя SharePoint и создать, если его нет
    3. создать изолированный домен приложений
    4. настроить DNS для адреса домена приложений

    На 3-ем пункте необходимо остановится подробней. Что такое домен приложения? При установке приложения (APP) в SharePoint ему выделяется уникальный адрес, по которому можно обратиться к этому App-приложению. Адрес выглядит так: http://[app prefix]-[app id].[domain name]/[site collection path]/[app path]/pages/default.aspx, где app prefix — это префикс приложений для всей фермы SharePoint; app id — уникальный идентификатор для приложения; domain name — домен приложений. Собственно конфигурация домена приложения — это и есть задание значений app prefix и domain name, и их можно было бы задать через Central Administration (Apps -> Configure App URLs), если бы не одно но — необходимо сначала создать два новых сервиса SubscriptionSettingsServiceApplication и SPAppManagementServiceApplication. Я приведу ниже немного усовершенствованный скрипт PowerShell для создания этих сервисов и конфигурирования настроек домена приложений:
    $appDomain = "your app domain"
    $appPrefix = "your app prefix"
    $accountName = "your account name"
    $accountPassword = "your account password"
    
    net start spadminv4 
    net start sptimerv4 
    
    $existedAppDomain = Get-SPAppDomain
    $existedAppPrefix = Get-SPAppSiteSubscriptionName
    
    if (!existedAppDomain || !existedAppPrefix)
    {
            if (!$existedAppDomain)
            {
               Set-SPAppDomain $appDomain -ea:Stop
            }
    
            Get-SPServiceInstance | where{$_.GetType().Name -eq "AppManagementServiceInstance" -or $_.GetType().Name -eq "SPSubscriptionSettingsServiceInstance"} | Start-SPServiceInstance
            Get-SPServiceInstance | where{$_.GetType().Name -eq "AppManagementServiceInstance" -or $_.GetType().Name -eq "SPSubscriptionSettingsServiceInstance"}
            $User = $accountName
            $account = Get-SPManagedAccount  -Identity $User -ea:Silently
            if(!$account)
            {
               $PWord = ConvertTo-SecureString –String $accountPassword –AsPlainText -Force
               $Credential = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $User, $PWord
               $account = New-SPManagedAccount -Credential $Credential -ea:Stop
            }
            
            $appPoolSubSvc = Get-SPServiceApplicationPool -Identity SettingsServiceAppPool 
            if (!$appPoolSubSvc)
            {
               $appPoolSubSvc = New-SPServiceApplicationPool -Name SettingsServiceAppPool -Account $account
            }
            $appPoolAppSvc = Get-SPServiceApplicationPool -Identity AppServiceAppPool 
            if ($appPoolAppSvc!)
            {
               $appPoolAppSvc = New-SPServiceApplicationPool -Name AppServiceAppPool -Account $account
            }
    
            $appSubSvc = New-SPSubscriptionSettingsServiceApplication –ApplicationPool $appPoolSubSvc –Name SettingsServiceApp –DatabaseName SettingsServiceDB 
            $proxySubSvc = New-SPSubscriptionSettingsServiceApplicationProxy –ServiceApplication $appSubSvc
            $appAppSvc = New-SPAppManagementServiceApplication -ApplicationPool $appPoolAppSvc -Name AppServiceApp -DatabaseName AppServiceDB
            $proxyAppSvc = New-SPAppManagementServiceApplicationProxy -ServiceApplication $appAppSvc
    
            Set-SPAppSiteSubscriptionName -Name $appPrefix -Confirm:$false -ea:Stop
    }
    

    Хочу предупредить, что последняя команда по какой-то причине очень ресурсоёмка и может потребовать все минимальные требования SharePoint к физическим составляющим сервера (“железу”). В моем случае команда несколько раз не выполнялась, т.к. было недостаточно свободной памяти у виртуальной машины. Если вы ранее переустанавливали SharePoint, то необходимо будет удалить существующие файлы баз данных SettingsServiceDB и AppServiceDB.

    Итак, SharePoint установлен и настроен для наших приложений. Теперь необходимо SharePoint сообщить о том, что наше Web-приложение будет доверенным. Подробно этот процесс описан здесь.
    Если излагать кратко, то необходимо
    1. создать самоподписанный сертификат IIS (этот сертификат подходит только для стадии debug, для полноценных решений потребуется доменный или коммерческий сертификат)
    2. выгрузить pfx и cer файлы
    3. загрузить сертификат в SharePoint и связать его с нашим Web-приложением

    На последнем пункте я остановлюсь и распишу подробней. По сути требуется выполнить следующий PowerShell скрипт:
    $publicCertPath = "publicCertPath *.cer"
    $appId = "app Id Guid"
    $spurl ="SharePoint site url"
    $appPrincipalDisplayName = "your app pricipal name"
    
    $spweb = Get-SPWeb $spurl
    $realm = Get-SPAuthenticationRealm -ServiceContext $spweb.Site
    $certificate = Get-PfxCertificate $publicCertPath
    $fullAppIdentifier = $appId + '@' + $realm
    $principal = Get-SPAppPrincipal –NameIdentifier $fullAppIdentifier -Site $spweb 
    if(!$principal)
    {
    	$issuer = Get-SPTrustedSecurityTokenIssuer | where {$_.NameId -eq $fullAppIdentifier} 
    	if ($issuer) 
    	{ 
    	Remove-SPTrustedSecurityTokenIssuer -Identity $issuer.Id -Confirm:$False -ea:Stop
    	}
    
    	New-SPTrustedSecurityTokenIssuer -Name $appPrincipalDisplayName -Certificate $certificate -RegisteredIssuerName $fullAppIdentifier -Confirm:$False -ea:Stop
    	$principal = Register-SPAppPrincipal -NameIdentifier $fullAppIdentifier -Site $spweb -DisplayName $appPrincipalDisplayName
    }
    Set-SPAppPrincipalPermission -Site $spweb -AppPrincipal $principal -Scope Site -Right FullControl
    iisreset
    

    После выполнения этого скрипта любое удаленное приложение c идентификатором $appId сможет получить доступ к элементам SharePoint через сертификат с приватным ключом (pfx).
    Здесь стоит запомнить app id. Это и есть идентификатор нашего Web-приложения. Он будет использоваться далее в web.config Web-приложения под именем IssuerId.

    Последним шагом будет создание SharePoint App приложения. Подробно этот процесс описан далее в статье. При создании проекта Wizard предложит заполнить ряд параметров: Issuer Id (сгенерированный нами app id), путь к сертификату pfx, пароль к сертификату. Созданное Web-приложение можно заменить на уже существующее в Visual Studio solution в свойствах проекта. После подсоединения проекта SharePoint и Web-приложения в Web-приложении в файле web.config заполняются параметры:
      <appSettings>
        <add key="IssuerId" value="your app id" />
        <add key="ClientSigningCertificatePath" value="your pfx file path" />
        <add key="ClientSigningCertificatePassword" value="pfx password" />
      </appSettings>
    

    Также в проекте Web-приложения появятся полезные классы SharePointContext.cs и TokenHelper.cs. Эти классы предоставляют довольно много возможностей для взаимодействия с SharePoint.

    На этом интеграция завершена. Если далее запустить проект SharePoint App через debug — SharePoint сначала запросит разрешения для нашего App. После принятия разрешений, пройдя через специальную страницу SharePoint (appredirect.aspx), мы попадем в корень нашего Web-приложения. Считается, что на этом процесс интеграции завершен, однако, как будет видно дальше — нас ожидает множество подводных камней.

    В следующих статьях я подробнее расскажу, как организовать взаимодействие с SharePoint (поиск и чтение документов и других элементов SharePoint), создать app-parts, локализовать App, создать свой RibbonTab, автоматизировать установку App & Solution и как подготовиться к публикации на SharePoint Store.
    • +4
    • 10.2k
    • 3
    ДоксВижн
    44.92
    Company
    Share post

    Comments 3

      0
      А такой вопрос. Допустим я создаю provider hosted приложение. У меня есть отличный UI на html5, который я хочу использовать. Но проблема в том что этот UI был подключен к Web API. Как организуете доступ к SharePoint из Web API?
        0
        Если вы хотите использовать именно provider-hosted приложение, то тут есть некоторые нюансы. Вообще я не сталкивался с такой задачей, однако изучив ваш вопрос вышел на следующую статью. В ней подробно описаны проблемы и как их решить. Коротко говоря, одно из отличий ApiController от MvcControler в том, что первый подразумевает статичность. А это значит, что ApiController не поддерживают сессию и HttpContext — это и является проблемой, т.к. оба компонента требуются для стандартного класса SharePointContext.cs. В статье предлагается использовать ActionAttribute, который бы проверял несет ли в себе ControllerContext информацию о SharePointContext и получать его через redirect к SharePoint сайту. Также придется переопределить SharePointContext на SharePointApiControllerContext, который не использует сессию и HttpContext.

        Пока я писал этот ответ мне в голову пришла другая идея. Как вариант вы могли бы использовать SharePoint-hosted приложение. В этом случае вам необходимо будет использовать клиентские скрипты Web-приложения внутри App для отрисковки UI на стороне SharePoint и обащаться через JSONP с вашим web-приложением. В данном случае наверняка потребуется дополнительное исследование в зависимости от того как вы собираетесь поставлять ваш App.
          0
          А где у вас расположен Web API (внтури корп сети или вне) и какая аутенитификация в WebAPI и SharePoint?

        Only users with full accounts can post comments. Log in, please.