如何从Angular通过浏览器URI启动UWP应用并传递对象实现自动登录
实现方案概述
核心基于UWP的*协议激活(Protocol Activation)*能力实现,整体流程为:UWP端提前注册自定义URI Scheme,Angular端构造带加密凭证的对应Scheme URI触发启动UWP,UWP端接收参数后解密校验,完成自动登录。
步骤1:UWP端注册自定义URI协议
- 打开UWP项目的
Package.appxmanifest文件,切换到「声明」标签页 - 新增「协议」声明,设置名称为自定义值(比如
myuwpapp),保存配置即可 - 重写
App.xaml.cs的OnActivated方法,用于接收启动URI参数:
protected override void OnActivated(IActivatedEventArgs args) { if (args.Kind == ActivationKind.Protocol) { ProtocolActivatedEventArgs protocolArgs = args as ProtocolActivatedEventArgs; Uri launchUri = protocolArgs.Uri; // 后续参数解析、解密逻辑在此处实现 } base.OnActivated(args); }
步骤2:Angular端实现凭证加密与URI构造
注意:禁止明文传递账号密码,统一使用AES对称加密,两端提前约定加密参数,生产环境密钥建议从后端动态获取,不要硬编码在前端代码中
- 安装加密依赖:
npm install crypto-js @types/crypto-js - 业务逻辑实现示例:
import * as CryptoJS from 'crypto-js'; // 启动UWP方法,传入用户账号密码 launchUWP(userId: string, password: string) { // 加密参数:密钥长度需符合AES要求(16/24/32位),IV固定为16位 const aesKey = '约定的AES加密密钥'; const aesIv = '约定的16位偏移量'; // 构造凭证对象,增加时间戳防止重放攻击 const credential = { userId, password, timestamp: new Date().getTime() }; // AES加密 const encryptedCred = CryptoJS.AES.encrypt( JSON.stringify(credential), CryptoJS.enc.Utf8.parse(aesKey), { iv: CryptoJS.enc.Utf8.parse(aesIv), mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } ).toString(); // URI编码避免特殊字符解析异常 const encodedCred = encodeURIComponent(encryptedCred); // 构造启动URI const launchUri = `myuwpapp://autoLogin?cred=${encodedCred}`; // 触发启动UWP window.location.href = launchUri; }
步骤3:UWP端实现参数解密与自动登录
UWP端需使用和Angular端完全一致的加密参数解密,示例代码如下:
- 首先安装JSON解析依赖:
Newtonsoft.JsonNuget包 - 解密与登录逻辑实现:
using System.Security.Cryptography; using System.Text; using Newtonsoft.Json; // 解密工具方法 private string AesDecrypt(string encryptedText, string key, string iv) { byte[] encryptedBytes = Convert.FromBase64String(encryptedText); using (Aes aes = Aes.Create()) { aes.Key = Encoding.UTF8.GetBytes(key); aes.IV = Encoding.UTF8.GetBytes(iv); aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); using (var ms = new MemoryStream()) using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write)) { cs.Write(encryptedBytes, 0, encryptedBytes.Length); cs.FlushFinalBlock(); return Encoding.UTF8.GetString(ms.ToArray()); } } } // 凭证实体类,和Angular端结构对应 public class CredentialModel { public string UserId { get; set; } public string Password { get; set; } public long Timestamp { get; set; } } // 完善OnActivated中的处理逻辑 protected override void OnActivated(IActivatedEventArgs args) { if (args.Kind == ActivationKind.Protocol) { ProtocolActivatedEventArgs protocolArgs = args as ProtocolActivatedEventArgs; var queryParams = HttpUtility.ParseQueryString(protocolArgs.Uri.Query); string encryptedCred = queryParams.Get("cred"); if (!string.IsNullOrEmpty(encryptedCred)) { try { // 使用和前端一致的密钥解密 string aesKey = "约定的AES加密密钥"; string aesIv = "约定的16位偏移量"; string decryptedStr = AesDecrypt(encryptedCred, aesKey, aesIv); var credential = JsonConvert.DeserializeObject<CredentialModel>(decryptedStr); // 校验凭证有效期,比如设置5分钟过期,防止重放攻击 long currentTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); if (currentTimestamp - credential.Timestamp > 5 * 60 * 1000) { // 凭证过期,跳转手动登录页 NavigateToLoginPage(); return; } // 校验账号密码有效性,验证通过则进入应用主页 bool isLoginValid = ValidateUserCredential(credential.UserId, credential.Password); if (isLoginValid) { NavigateToMainPage(); } else { NavigateToLoginPage(); } } catch { // 解密失败/参数非法,跳转手动登录页 NavigateToLoginPage(); } } else { NavigateToLoginPage(); } } base.OnActivated(args); }
注意事项
- 首次触发URI启动时,浏览器会弹出确认弹窗询问用户是否允许启动对应UWP应用,属于浏览器安全策略限制,无法通过代码绕过
- 加密密钥不要硬编码在代码中:UWP端可以存到Windows凭据管理器,Angular端生产环境建议由后端接口签发短期有效密钥,降低泄露风险
- 如果传递的参数较长,可以先压缩再加密,避免超出浏览器URI长度限制
- 可额外增加签名校验:对加密后的凭证生成签名一并传递,UWP端先校验签名合法性再解密,防止参数被篡改
内容的提问来源于stack exchange,提问作者Shreyas
相关产品推荐
相关产品推荐

