Quellcode durchsuchen

调整省平台单点登录。

CrazyIter_Bin vor 3 Jahren
Ursprung
Commit
5ca01b92bc

+ 3 - 1
TEAMModelOS.SDK/Models/Cosmos/Common/StuActivity.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using System.Text.Json;
 
 namespace TEAMModelOS.SDK.Models
 {
@@ -65,6 +66,7 @@ namespace TEAMModelOS.SDK.Models
         /// 任务完成状态,-1 未参与,0,未完成, 1已完成
         /// </summary>
         public int taskStatus { get; set; } = -1;
-        //写入投票记录
+        //写入投票记录,评测作答记录等,自行定义相关规范
+        public List<JsonElement> extParam { get; set; } = new List<JsonElement>();
     }
 }

+ 11 - 3
TEAMModelOS.SDK/Models/Cosmos/Teacher/Teacher.cs

@@ -31,9 +31,17 @@ namespace TEAMModelOS.SDK.Models
         }
         public class ThirdBind
         {
-            public string webid { get; set; }
-            public string pxid { get; set; }
-            public string tid { get; set; }
+            /// <summary>
+            /// scsyxpt 四川省研修平台
+            /// </summary>
+            public string type { get; set; }
+            /// <summary>
+            /// 用户来源
+            /// </summary>
+            public string source { get; set; }
+          
+            public string userid { get; set; }
+            public HashSet<string> pxid { get; set; } = new HashSet<string>();
 
         }
     }

+ 2 - 2
TEAMModelOS.SDK/Models/Service/AccountHttpService.cs

@@ -10,10 +10,10 @@ using TEAMModelOS.SDK.Extension;
 
 namespace TEAMModelOS.SDK.Models.Service
 {
-    public class AccountHttpService
+    public class CoreAPIHttpService
     {
         private readonly HttpClient _httpClient;
-        public AccountHttpService(HttpClient httpClient)
+        public CoreAPIHttpService(HttpClient httpClient)
         {
             _httpClient = httpClient;
         }

+ 253 - 0
TEAMModelOS/Controllers/Third/ScController.cs

@@ -0,0 +1,253 @@
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using TEAMModelOS.Models;
+using TEAMModelOS.SDK.DI;
+using System.Text.Json;
+using TEAMModelOS.SDK.Models;
+using Microsoft.AspNetCore.Http;
+using TEAMModelOS.SDK.Extension;
+using Azure.Cosmos;
+using System.Text;
+using TEAMModelOS.SDK.DI.AzureCosmos.Inner;
+using Microsoft.Extensions.Options;
+using Azure.Messaging.ServiceBus;
+using Microsoft.Extensions.Configuration;
+using TEAMModelOS.Services.Common;
+using HTEXLib.COMM.Helpers;
+using TEAMModelOS.SDK;
+using System.IdentityModel.Tokens.Jwt;
+using TEAMModelOS.Services;
+using TEAMModelOS.SDK.Models.Service;
+using System.IO;
+using System.Dynamic;
+using Microsoft.AspNetCore.Authorization;
+using Azure.Storage.Blobs.Models;
+
+namespace TEAMModelOS.Controllers.Third
+{
+
+    /// <summary>
+    /// 
+    /// </summary>
+    ///  
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
+    //[Authorize(Roles = "IES5")]
+    [Route("sc")]
+    //[Route("api/[controller]")]
+    [ApiController]
+    public class ScController : ControllerBase
+    {
+        private readonly SnowflakeId _snowflakeId;
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureServiceBusFactory _serviceBus;
+        private readonly AzureRedisFactory _azureRedis;
+        private readonly CoreAPIHttpService _accountHttpService;
+        public readonly string type = "scsyxpt";
+        public IConfiguration _configuration { get; set; }
+        public ScController(AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage,
+          AzureRedisFactory azureRedis, AzureServiceBusFactory serviceBus, IConfiguration configuration, CoreAPIHttpService accountHttpService)
+        {
+            _azureCosmos = azureCosmos;
+            _snowflakeId = snowflakeId;
+            _dingDing = dingDing;
+            _option = option?.Value;
+            _azureStorage = azureStorage;
+            _serviceBus = serviceBus;
+            _configuration = configuration;
+            _azureRedis = azureRedis;
+            _accountHttpService = accountHttpService;
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("bind")]
+        [AllowAnonymous]
+        public async Task<IActionResult> Bind(SSO sso) {
+            var rurl = new StringBuilder($"https://{_option.HostName}/sso");
+            try 
+            {
+                Teacher teacher = null;
+                if (string.IsNullOrEmpty(sso.idToken)) {
+                    return Redirect(rurl.Append($"?status=1").ToString());
+                }
+                var jwt = new JwtSecurityToken(sso.idToken);
+                if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
+                var id = jwt.Payload.Sub;
+                jwt.Payload.TryGetValue("name", out object name);
+                jwt.Payload.TryGetValue("picture", out object picture);
+                var client = _azureCosmos.GetCosmosClient();
+                teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
+                //先检查绑定的平台是否已经被绑定
+                //四川研训平台跳转隐式登录/或者绑定IES平台接入规范
+                ScSSO scsso= sso.param.ToObject<ScSSO>();
+                string sql = $"SELECT distinct value(c) FROM c join A1 in  c.binds where  A1.webid='{scsso.Webid}' and A1.tid='{scsso.tid}'";
+                await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Teacher>(queryText: sql,
+                    requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
+                {
+                    teacher = item;
+                    break;
+                }
+                if (teacher != null)
+                {
+                    var bind = teacher.binds.Find(x => x.source.Equals($"{scsso.Webid}") && x.userid.Equals($"{scsso.tid}"));
+                    if (bind != null)
+                    {
+                        teacher.binds.Add(new Teacher.ThirdBind { pxid = new HashSet<string> { $"{scsso.Pxid}" }, userid = $"{scsso.tid}", source = $"{scsso.Webid}", type = type });
+                        await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey(teacher.code));
+                    }
+                    else
+                    {
+                        if (bind.pxid.Add(scsso.Pxid))
+                        {
+                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey(teacher.code));
+                        }
+                    }
+                }
+                else {
+                    teacher = new Teacher
+                    {
+                        id = id,
+                        pk = "Base",
+                        code = "Base",
+                        name = name?.ToString(),
+                        picture = picture?.ToString(),
+                        //创建账号并第一次登录IES5则默认赠送1G
+                        size = 1,
+                        defaultSchool = null,
+                        schools = new List<Teacher.TeacherSchool>(),
+                        binds= new List<Teacher.ThirdBind> { new Teacher.ThirdBind { pxid = new HashSet<string> { $"{scsso.Pxid}" }, userid = $"{scsso.tid}", source = $"{scsso.Webid}", type = type } }
+                    };
+                    var container = _azureStorage.GetBlobContainerClient(id);
+                    await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
+                    teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
+                }
+                return Ok(new
+                {
+                    location = _option.Location,
+                    status = 200,
+                });
+                
+            } catch (Exception ex) { 
+            
+            }
+            return Redirect(rurl.Append($"?status=1").ToString());
+
+        }
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+
+        [HttpGet("sso")]
+        [AllowAnonymous]
+        public async Task<IActionResult> Sso([FromQuery] ScSSO sso)
+        {
+            var rurl = new StringBuilder($"https://{_option.HostName}/sso");
+            string parmas = $"Pxid={sso.Pxid}&Webid={sso.Webid}&tid={sso.tid}&time={sso.time}";
+            if (Md5Hash.GetMd5String(parmas).Equals($"{sso.Encrypt}"))
+            {
+                //四川研训平台跳转隐式登录/或者绑定IES平台接入规范
+              
+                long ssotime = long.Parse($"{sso.time}");
+                long nowtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
+                if (nowtime - ssotime > 60 * 10)//10分钟有效期
+                {
+                    //  return Ok(new { status = 2, msg = "登录超时!" });
+                }
+            }
+            else
+            {
+                return Redirect(rurl.Append($"?status=1").ToString());
+            }
+            Teacher teacher = null;
+            //四川研训平台跳转隐式登录/或者绑定IES平台接入规范
+            //string sql = $"SELECT distinct value(c) FROM c join A1 in  c.binds where A1.pxid='{sso.Pxid}' and A1.webid='{sso.Webid}' and A1.tid='{sso.tid}'";
+            string sql = $"SELECT distinct value(c) FROM c join A1 in  c.binds where  A1.source='{sso.Webid}' and A1.userid='{sso.tid}'";
+            await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Teacher>(queryText: sql,
+                requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
+            {
+                teacher = item;
+                break;
+            }
+            if (teacher == null)
+            {
+                return Redirect(rurl.Append($"?status=0&param={sso.ToJsonString()}&type={type}&bindurl=sc/bind").ToString());
+            }
+            else
+            {
+                var url = _configuration.GetValue<string>("HaBookAuth:CoreAPI");
+                var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
+                var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
+                var location = _option.Location;
+                (int code, string content) = await _accountHttpService.Implicit(clientID, clientSecret, location, $"{url}/oauth2/implicit",
+                    new Dictionary<string, string>()
+                    {
+                                    { "grant_type", "implicit" },
+                                    { "client_id",clientID },
+                                    { "account",teacher.id },
+                                    { "nonce",Guid.NewGuid().ToString()}
+                    });
+                TmdidImplicit implicit_token = new TmdidImplicit();
+                if (!string.IsNullOrEmpty(content) && code==200)
+                {
+                    implicit_token = content.ToObject<TmdidImplicit>();
+                    var bind = teacher.binds.Find(x => x.userid.Equals(sso.tid) && x.source.Equals(sso.Webid));
+                    if (bind != null) {
+                        if (bind.pxid != null)
+                        {
+                            if (bind.pxid.Add(sso.Pxid))
+                            {
+                                await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey(teacher.code));
+                            }
+                        }
+                        else {
+                            bind.pxid = new HashSet<string> { sso.Pxid };
+                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey(teacher.code));
+                        }
+                    }
+                    return Redirect(rurl.Append($"?status=200&id_token={implicit_token.id_token}&access_token={implicit_token.access_token}&expires_in={implicit_token.expires_in}&token_type={implicit_token.token_type}").ToString());
+                }
+                else {
+                    //绑定失效
+                    if (teacher.binds.IsNotEmpty()) {
+                        teacher.binds.RemoveAll(x => x.userid.Equals(sso.tid) && x.source.Equals(sso.Webid));
+                        await  _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher,teacher.id,new PartitionKey(teacher.code));
+                    }
+                    return Redirect(rurl.Append($"?status=0&param={sso.ToJsonString()}&type={type}&bindurl=sc/bind").ToString());
+                }
+            }
+        }
+        public record ScSSO{
+            public string Webid { get; set; }
+            public string Pxid { get; set; }
+            public string tid { get; set; }
+            public string time { get; set; }
+            public string Encrypt { get; set; }
+            public string idToken { get; set; }
+        }
+        public record SSO
+        {
+            public string type { get; set; }
+            public string param { get; set; }
+            public string idToken { get; set; }
+        }
+        public record TmdidImplicit { 
+            public string id_token { get; set; }
+            public string access_token { get; set; }
+            public string expires_in { get; set; }
+            public string token_type { get; set; }
+        }
+    }
+}

+ 17 - 7
TEAMModelOS/Controllers/Third/ScTrainController.cs

@@ -33,10 +33,10 @@ namespace TEAMModelOS.Controllers
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status400BadRequest)]
     //[Authorize(Roles = "IES5")]
-    [Route("third/sso")]
+    [Route("sso")]
     //[Route("api/[controller]")]
     [ApiController]
-    public class ScTrainController : ControllerBase
+    public class SsoController : ControllerBase
     {
         private readonly SnowflakeId _snowflakeId;
         private readonly AzureCosmosFactory _azureCosmos;
@@ -45,10 +45,10 @@ namespace TEAMModelOS.Controllers
         private readonly AzureStorageFactory _azureStorage;
         private readonly AzureServiceBusFactory _serviceBus;
         private readonly AzureRedisFactory _azureRedis;
-        private readonly AccountHttpService _accountHttpService;
+        private readonly CoreAPIHttpService _accountHttpService;
         public IConfiguration _configuration { get; set; }
-        public ScTrainController(AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage,
-          AzureRedisFactory azureRedis, AzureServiceBusFactory serviceBus, IConfiguration configuration, AccountHttpService accountHttpService)
+        public SsoController(AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage,
+          AzureRedisFactory azureRedis, AzureServiceBusFactory serviceBus, IConfiguration configuration, CoreAPIHttpService accountHttpService)
         {
             _azureCosmos = azureCosmos;
             _snowflakeId = snowflakeId;
@@ -158,11 +158,21 @@ namespace TEAMModelOS.Controllers
                             teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
                         }
                         if (type.Equals("scsyxpt")) {
+                            //先检查绑定的平台是否已经被绑定
                             //四川研训平台跳转隐式登录/或者绑定IES平台接入规范
-                            var bind = teacher.binds.FindAll(x => x.pxid.Equals($"{_Pxid}") && x.webid.Equals($"{_Webid}") && x.tid.Equals($"{_tid}"));
+                            string sql = $"SELECT distinct value(c) FROM c join A1 in  c.binds where A1.pxid='{_Pxid}' and A1.webid='{_Webid}' and A1.tid='{_tid}'";
+                            await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Teacher>(queryText: sql,
+                                requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
+                            {
+                                teacher = item;
+                                break;
+                            }
+
+                            //四川研训平台跳转隐式登录/或者绑定IES平台接入规范
+                            var bind = teacher.binds.FindAll(x => x.pxid.Equals($"{_Pxid}") && x.source.Equals($"{_Webid}") && x.userid.Equals($"{_tid}"));
                             if (bind.IsEmpty())
                             {
-                                teacher.binds.Add(new Teacher.ThirdBind { pxid = $"{_Pxid}", tid = $"{_tid}", webid = $"{_Webid}" });
+                                teacher.binds.Add(new Teacher.ThirdBind { pxid =new HashSet<string> { $"{_Pxid}" }, userid = $"{_tid}", source = $"{_Webid}" , type = type });
                             }
                         }
                         await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>( teacher, id, new PartitionKey("Base"));

+ 1 - 1
TEAMModelOS/Startup.cs

@@ -106,7 +106,7 @@ namespace TEAMModelOS
             services.AddHttpClient();
             services.AddHttpClient<DingDing>();
             services.AddHttpClient<NotificationService>();
-            services.AddHttpClient<AccountHttpService>();
+            services.AddHttpClient<CoreAPIHttpService>();
             services.AddMemoryCache();
             services.AddSpaStaticFiles(opt => opt.RootPath = "ClientApp/dist");
             services.AddControllers().AddJsonOptions(options => { options.JsonSerializerOptions.IgnoreNullValues = false; });