用于 Azure 迁移的 MSIX Windows 10 应用程序访问被拒绝后 SSO 登录

Posted

技术标签:

【中文标题】用于 Azure 迁移的 MSIX Windows 10 应用程序访问被拒绝后 SSO 登录【英文标题】:MSIX Windows 10 App Access Denied post-SSO sign-in for Azure migration 【发布时间】:2021-07-16 23:40:14 【问题描述】:

我们正在将我们的应用程序迁移到 Azure。

我们为安装到 Windows 10 C:\Program Files\WindowsApps\ 文件夹的内部 WPF 应用程序创建了 MSIX 安装程序。当我们运行该应用程序时,它要求我们通过在线 Windows/Azure 门户网站输入我们的单点登录 (SSO) 凭据。成功输入我们的凭据后,我们会收到以下弹出窗口,提示访问文件被拒绝(见下文)。不管是正常运行还是as administrator,都会出现这个错误。

我们无法在网上找到任何有助于解决错误的信息。我们确实尝试获取此受保护文件夹的所有权,然后取消选中只读选项,但这不起作用(这听起来也不是一个好主意,但这是故障排除)。我们在 MSIX 安装项目中看不到任何可以解决此问题的内容。有谁知道我们为什么会收到这个错误以及如何解决它?

在事件查看器中,提供以下信息:

【问题讨论】:

【参考方案1】:

MSIX 打包应用不支持对安装文件夹 (WindowsApps) 中文件的写访问权限。这是设计使然,操作系统不允许获取所有权和强制更改文件。

应用程序运行时需要更改(更新)的任何配置文件都应保存在 AppData 用户文件夹或 CommonApplicationData 机器文件夹中(如果多个用户需要访问此配置/文件)。

我已经在这里写了关于这个主题的更长的解释:

How to allow for editable .Net generated config files with MSIX?

附注一般而言,将用户数据文件保存在安装文件夹中是一种不好的做法,即使您不使用 MSIX 部署应用程序也是如此。应用程序文件和应用程序数据(配置、用户数据等)之间的逻辑分离有助于确保您的客户可以在不丢失数据/配置的情况下顺利升级您的应用程序,如果旧 PC 崩溃,可以轻松迁移到新 PC,可以在虚拟环境(企业客户端)中运行应用程序,您对支持的烦恼就会减少;)。

【讨论】:

【参考方案2】:

我们无法在网上找到任何有助于解决错误的信息。

docs 列出了在将现有安装程序转换为 MSIX 之前需要了解的事项:

您的应用程序写入您的应用程序的安装目录。例如,您的应用程序写入一个日志文件,该文件与您的 exe 放在同一目录中。不支持此操作,因为该文件夹受保护。我们建议写入其他位置,例如本地应用程序数据存储。我们在 1809 及更高版本中添加了一项功能。

解决方案显然是将文件保存在您的应用确实具有写入权限的另一个文件夹中。 MSIX 打包应用永远无法将文件写入安装文件夹。

【讨论】:

【参考方案3】:

我发现了问题 - 需要将令牌存储在缓存文件中。当我在 Google 上搜索 msalcache 时,它以 TokenCacheHelper 的形式返回,它位于堆栈跟踪中。该文件似乎是使用下面的代码输出自动生成的。

//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.
// All rights reserved.
//
// This code is licensed under the MIT License.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//------------------------------------------------------------------------------

using Microsoft.Identity.Client;
using System.IO;
using System.Runtime.Versioning;
using System.Security.Cryptography;

namespace <AppName>.Helpers

    static class TokenCacheHelper
    
        /// <summary>
        /// Path to the token cache
        /// </summary>
        public static readonly string CacheFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location + ".msalcache.bin3";

        private static readonly object FileLock = new object();

        public static void BeforeAccessNotification(TokenCacheNotificationArgs args)
        
            lock (FileLock)
            
                args.TokenCache.DeserializeMsalV3(File.Exists(CacheFilePath)
                        ? ProtectedData.Unprotect(File.ReadAllBytes(CacheFilePath),
                                                 null,
                                                 DataProtectionScope.CurrentUser)
                        : null);
            
        

        public static void AfterAccessNotification(TokenCacheNotificationArgs args)
        
            // if the access operation resulted in a cache update
            if (args.HasStateChanged)
            
                lock (FileLock)
                
                    // reflect changesgs in the persistent store
                    File.WriteAllBytes(CacheFilePath,
                                       ProtectedData.Protect(args.TokenCache.SerializeMsalV3(),
                                                             null,
                                                             DataProtectionScope.CurrentUser)
                                      );
                
            
        

        internal static void EnableSerialization(ITokenCache tokenCache)
        
            tokenCache.SetBeforeAccess(BeforeAccessNotification);
            tokenCache.SetAfterAccess(AfterAccessNotification);
        
    

经过一番搜索,我找到了这两个相关的链接:

https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-net-token-cache-serialization#simple-token-cache-serialization-msal-only https://github.com/MicrosoftDocs/azure-docs/issues/49182

有问题的相关代码是针对CacheFilePath,实际上存储在注释中:

/// <summary>
/// Path to the token cache. Note that this could be something different for instance for MSIX applications:
/// private static readonly string CacheFilePath =
/// $"Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)\AppName\msalcache.bin";
/// </summary>
public static readonly string CacheFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location + ".msalcache.bin3";

CacheFilePath 的推荐修复实际上是无效的。所以,我做了以下修改:

private static readonly string AppName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
private static readonly string ApplicationDataFolder = $"Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)\\AppName\\";
private static readonly string CacheFilePath = $"ApplicationDataFolder\\msalcache.bin";

然后我添加了以下方法:

public static void CreateApplicationDataDirectory()

    FileInfo fileInfo = new FileInfo(ApplicationDataFolder);

    // Check to see if the directory exists. If it does not then create it. If we do not do this then the token CacheFilePath will
    // not be created.
    if (!fileInfo.Exists)
        Directory.CreateDirectory(fileInfo.Directory.FullName);

然后我修改了App.Xaml.cs 文件以在@​​987654333@ 进程之后立即调用CreateApplicationDataDirectory

_clientApp = PublicClientApplicationBuilder.Create(Params.ClientId)
            .WithAuthority(AzureCloudInstance.AzurePublic, Params.Tenant)
            .WithRedirectUri("http://localhost:1234")
            .Build();
TokenCacheHelper.CreateApplicationDataDirectory();
TokenCacheHelper.EnableSerialization(_clientApp.UserTokenCache);

【讨论】:

有帮助...为我工作。

以上是关于用于 Azure 迁移的 MSIX Windows 10 应用程序访问被拒绝后 SSO 登录的主要内容,如果未能解决你的问题,请参考以下文章

MSIX Core解决方案构建错误

“解析应用程序包时出错。”从 Web (MSIX) 打开 Windows 10 .appinstaller 文件时

将本地 WCF 服务迁移到 Windows Azure 需要考虑的事项

MSIX:设置安装路径

为 CakePHP Web 应用程序将 phpMyAdmin 数据库迁移到 Windows Azure

如何从 WPF MSIX 应用程序(桌面桥)更改 Windows 10 颜色设置?