Java微服务-WebApi公开接口请求签名验证_每日资讯
前言
现在的系统后端开发的时候,会公开很多API接口
对于要登录认证后才能访问的接口,这样的请求验证就由身份认证模块完成
但是也有些接口是对外公开的,没有身份认证的接口
我们怎么保证接口的请求是合法的,有效的.
这样我们一般就是对请求的合法性做签名验证.
实现原理
为保证接口安全,每次请求必带以下header
(资料图片)
| header名 | 类型 | 描述 |
| AppId | string | 应用Id |
| Ticks | string | 时间戳为1970年1月1日到现在时间的毫秒数(UTC时间) |
| RequestId | string | GUID字符串,作为请求唯一标志,防止重复请求 |
| Sign| string | 签名,签名算法如下 |
后端验证实现
验证AppId
先验证AppId是不是有,没有就直接返回失败如果有的话,就去缓存里取AppID对应的配置(如果缓存里没有,就去配置文件里取)如果没有对应AppId的配置,说明不是正确的请求,返回失败model.AppId = context.Request.Headers["AppId"]; if (String.IsNullOrEmpty(model.AppId)) { await this.ResponseValidFailedAsync(context, 501); return; } var cacheSvc = context.RequestServices.GetRequiredService<IMemoryCache>(); var cacheAppIdKey = #34;RequestValidSign:APPID:{model.AppId}"; var curConfig = cacheSvc.GetOrCreate<AppConfigModel>(cacheAppIdKey, (e) => { e.SlidingExpiration = TimeSpan.FromHours(1); var configuration = context.RequestServices.GetRequiredService<IConfiguration>(); var listAppConfig = configuration.GetSection(AppConfigModel.ConfigSectionKey).Get<AppConfigModel[]>(); return listAppConfig.SingleOrDefault(x => x.AppId == model.AppId); }); if (curConfig == null) { await this.ResponseValidFailedAsync(context, 502); return; }
验证时间戳
验证时间戳是不是有在请求头里传过来,没有就返回失败验证时间戳与当前时间比较,如果不在过期时间(5分钟)之内的请求,就返回失败时间戳为1970年1月1日到现在时间的毫秒数(UTC时间)var ticksString = context.Request.Headers["Ticks"].ToString(); if (String.IsNullOrEmpty(ticksString)) { await this.ResponseValidFailedAsync(context, 503); return; } model.Ticks = long.Parse(context.Request.Headers["Ticks"].ToString()); var diffTime = DateTime.UtcNow - (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(model.Ticks)); var expirTime = TimeSpan.FromSeconds(300);//过期时间 if (diffTime > expirTime) { await this.ResponseValidFailedAsync(context, 504); return; }
验证请求ID
验证请求ID是不是有在请求头里传过来,没有就返回失败验证请求ID是不是已经在缓存里存在,如果存在就表示重复请求,那么就返回失败如果请求ID在缓存中不存在,那么就表示正常的请求,同时把请求ID添加到缓存model.RequestId = context.Request.Headers["RequestId"]; if (String.IsNullOrEmpty(model.RequestId)) { await this.ResponseValidFailedAsync(context, 505); return; } var cacheKey = #34;RequestValidSign:RequestId:{model.AppId}:{model.RequestId}"; if (cacheSvc.TryGetValue(cacheKey, out _)) { await this.ResponseValidFailedAsync(context, 506); return; } else cacheSvc.Set(cacheKey, model.RequestId, expirTime);
验证签名
1.验证签名是否正常
2.签名字符串是#34;{AppId}{Ticks}{RequestId}{AppSecret}"组成
3.然后把签名字符串做MD5,再与请求传过来的Sign签名对比
4.如果一至就表示正常请求,请求通过。如果不一至,返回失败
public bool Valid() { var validStr = #34;{AppId}{Ticks}{RequestId}{AppSecret}"; return validStr.ToMD5String() == Sign; } model.Sign = context.Request.Headers["Sign"]; if (!model.Valid()) { await this.ResponseValidFailedAsync(context, 507); return; }
源代码
我们把所有代码写成一个Asp.Net Core的中间件
/// <summary>/// 请求签名验证/// </summary>public class RequestValidSignMiddleware{ private readonly RequestDelegate _next; public RequestValidSignMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { var model = new RequestValidSignModel(); //1.先验证AppId是不是有,没有就直接返回失败 //2.如果有的话,就去缓存里取AppID对应的配置(如果缓存里没有,就去配置文件里取) //3.如果没有对应AppId的配置,说明不是正确的请求,返回失败 model.AppId = context.Request.Headers["AppId"]; if (String.IsNullOrEmpty(model.AppId)) { await this.ResponseValidFailedAsync(context, 501); return; } var cacheSvc = context.RequestServices.GetRequiredService<IMemoryCache>(); var cacheAppIdKey = #34;RequestValidSign:APPID:{model.AppId}"; var curConfig = cacheSvc.GetOrCreate<AppConfigModel>(cacheAppIdKey, (e) => { e.SlidingExpiration = TimeSpan.FromHours(1); var configuration = context.RequestServices.GetRequiredService<IConfiguration>(); var listAppConfig = configuration.GetSection(AppConfigModel.ConfigSectionKey).Get<AppConfigModel[]>(); return listAppConfig.SingleOrDefault(x => x.AppId == model.AppId); }); if (curConfig == null) { await this.ResponseValidFailedAsync(context, 502); return; } //1.把缓存/配置里面的APP配置取出来,拿到AppSecret //2.如果请求里附带了AppSecret(调试用),那么就只验证AppSecret是否正确 //3.传过来的AppSecret必需是Base64编码后的 //4.然后比对传过来的AppSecret是否与配置的AppSecret一至,如果一至就通过,不一至就返回失败 //5.如果请求里没有附带AppSecret,那么走其它验证逻辑. model.AppSecret = curConfig.AppSecret; var headerSecret = context.Request.Headers["AppSecret"].ToString(); if (!String.IsNullOrEmpty(headerSecret)) { var secretBuffer = new byte[1024]; var secretIsBase64 = Convert.TryFromBase64String(headerSecret, new Span<byte>(secretBuffer), out var bytesWritten); if (secretIsBase64 && Encoding.UTF8.GetString(secretBuffer, 0, bytesWritten) == curConfig.AppSecret) await _next(context); else { await this.ResponseValidFailedAsync(context, 508); return; } } else { //1.验证时间戳是不是有在请求头里传过来,没有就返回失败 //2.验证时间戳与当前时间比较,如果不在过期时间(5分钟)之内的请求,就返回失败 //时间戳为1970年1月1日到现在时间的毫秒数(UTC时间) var ticksString = context.Request.Headers["Ticks"].ToString(); if (String.IsNullOrEmpty(ticksString)) { await this.ResponseValidFailedAsync(context, 503); return; } model.Ticks = long.Parse(context.Request.Headers["Ticks"].ToString()); var diffTime = DateTime.UtcNow - (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(model.Ticks)); var expirTime = TimeSpan.FromSeconds(300);//过期时间 if (diffTime > expirTime) { await this.ResponseValidFailedAsync(context, 504); return; } //1.验证请求ID是不是有在请求头里传过来,没有就返回失败 //2.验证请求ID是不是已经在缓存里存在,如果存在就表示重复请求,那么就返回失败 //3.如果请求ID在缓存中不存在,那么就表示正常的请求,同时把请求ID添加到缓存 model.RequestId = context.Request.Headers["RequestId"]; if (String.IsNullOrEmpty(model.RequestId)) { await this.ResponseValidFailedAsync(context, 505); return; } var cacheKey = #34;RequestValidSign:RequestId:{model.AppId}:{model.RequestId}"; if (cacheSvc.TryGetValue(cacheKey, out _)) { await this.ResponseValidFailedAsync(context, 506); return; } else cacheSvc.Set(cacheKey, model.RequestId, expirTime); //1.验证签名是否正常 //2.签名字符串是#34;{AppId}{Ticks}{RequestId}{AppSecret}"组成 //3.然后把签名字符串做MD5,再与请求传过来的Sign签名对比 //4.如果一至就表示正常请求,请求通过。如果不一至,返回失败 model.Sign = context.Request.Headers["Sign"]; if (!model.Valid()) { await this.ResponseValidFailedAsync(context, 507); return; } await _next(context); } } /// <summary> /// 返回验证失败 /// </summary> /// <param name="context"></param> /// <param name="status"></param> /// <returns></returns> public async Task ResponseValidFailedAsync(HttpContext context, int status) { context.Response.StatusCode = 500; await context.Response.WriteAsJsonAsync(new ComResult() { Success = false, Status = status, Msg = "请求签名验证失败" }, Extention.DefaultJsonSerializerOptions, context.RequestAborted); }}public class AppConfigModel{ public const string ConfigSectionKey = "AppConfig"; /// <summary> /// 应用Id /// </summary> public string AppId { get; set; } /// <summary> /// 应用密钥 /// </summary> public string AppSecret { get; set; }}public class RequestValidSignModel : AppConfigModel{ /// <summary> /// 前端时间戳 /// Date.now() /// 1970 年 1 月 1 日 00:00:00 (UTC) 到当前时间的毫秒数 /// </summary> public long Ticks { get; set; } /// <summary> /// 请求ID /// </summary> public string RequestId { get; set; } /// <summary> /// 签名 /// </summary> public string Sign { get; set; } public bool Valid() { var validStr = #34;{AppId}{Ticks}{RequestId}{AppSecret}"; return validStr.ToMD5String() == Sign; }}
中间件注册扩展
写一个中间件的扩展,这样我们在Program里可以方便的使用/停用中间件
/// <summary>/// 中间件注册扩展/// </summary>public static class RequestValidSignMiddlewareExtensions{ public static IApplicationBuilder UseRequestValidSign(this IApplicationBuilder builder) { return builder.UseMiddleware<RequestValidSignMiddleware>(); }}///Program.csapp.UseRequestValidSign();
与Swagger结合
我们一般对外提供在线的Swagger文档
如果我们增加了请求验证的Header,那么所有接口文档里面都要把验证的Header添加到在线文档里面
/// <summary>/// 请求签名验证添加Swagger请求头/// </summary>public class RequestValidSignSwaggerOperationFilter : IOperationFilter{ public void Apply(OpenApiOperation operation, OperationFilterContext context) { if (operation.Parameters == null) operation.Parameters = new List<OpenApiParameter>(); operation.Parameters.Add(new OpenApiParameter { Name = "AppId", In = ParameterLocation.Header, Required = true, Description = "应用ID", Schema = new OpenApiSchema { Type = "string" } }); operation.Parameters.Add(new OpenApiParameter { Name = "Ticks", In = ParameterLocation.Header, Required = true, Description = "时间戳", Example = new OpenApiString(((long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds).ToString()), Schema = new OpenApiSchema { Type = "string" } }); operation.Parameters.Add(new OpenApiParameter { Name = "RequestId", In = ParameterLocation.Header, Required = true, Description = "请求ID", Example = new OpenApiString(Guid.NewGuid().ToString()), Schema = new OpenApiSchema { Type = "string" } }); operation.Parameters.Add(new OpenApiParameter { Name = "Sign", In = ParameterLocation.Header, Required = true, Description = "请求签名", //{AppId}{Ticks}{RequestId}{AppSecret} Example = new OpenApiString("MD5({AppId}{Ticks}{RequestId}{AppSecret})"), Schema = new OpenApiSchema { Type = "string" } }); operation.Parameters.Add(new OpenApiParameter { Name = "AppSecret", In = ParameterLocation.Header, Description = "应用密钥(调试用)", Example = new OpenApiString("BASE64({AppSecret})"), Schema = new OpenApiSchema { Type = "string" } }); }}///在Program.cs里添加Swagger请求验证Headerbuilder.Services.AddSwaggerGen(c =>{ c.OperationFilter<RequestValidSignSwaggerOperationFilter>();});
客户端调用实现
我们如果用HttpClient调用的话,就要在调用请求前
设置后请求头,AppId,Ticks,RequestId,Sign
public async Task<string> GetIPAsync(CancellationToken token) { this.SetSignHeader(); var result = await Client.GetStringAsync("/Get", token); return result; } public void SetSignHeader() { this.Client.DefaultRequestHeaders.Clear(); var ticks = ((long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds).ToString(); var requestId = Guid.NewGuid().ToString(); var signString = #34;{this.Config.AppId}{ticks}{requestId}{this.Config.AppSecret}"; var sign = this.GetMD5(signString); this.Client.DefaultRequestHeaders.Add("AppId", this.Config.AppId); this.Client.DefaultRequestHeaders.Add("Ticks", ticks); this.Client.DefaultRequestHeaders.Add("RequestId", requestId); this.Client.DefaultRequestHeaders.Add("Sign", sign); } public string GetMD5(string value) { using (MD5 md5 = MD5.Create()) { byte[] inputBytes = Encoding.UTF8.GetBytes(value); byte[] hashBytes = md5.ComputeHash(inputBytes); StringBuilder sb = new StringBuilder(); for (int i = 0; i < hashBytes.Length; i++) { sb.Append(hashBytes[i].ToString("x2")); } return sb.ToString(); } }
最终效果
当我们没有传签名参数的时候,返回失败
当我们把签名参数都传正确后,返回正确
都看完了,你确定不点个赞,关注下再走?
标签:
上一篇:春兴精工7月3日快速回调_信息
下一篇:最后一页
- Java微服务-WebApi公开接口请求签名验证_每日资讯
- 春兴精工7月3日快速回调_信息
- 古鳌科技7月3日盘中涨幅达5% 实时焦点
- 轴承外径尺寸公差表_h11公差表 天天日报
- 东莞麻涌镇25个动工竣工项目,总投资约178亿元_当前讯息
- 全球数字经济大会人工智能高峰论坛率先亮相 国内公开大模型北京约占一半
- 怀旧服如何退出战场_怀旧服 如何退出战场
- 环球视讯!魔芋好消化吗(魔芋的吃法)
- 逆水寒手游驰援飞起台怎么过 逆水寒手游驰援飞起台攻略详解
- 今头条!使命召唤手游硬派抢牌模式武器怎么选
- 天天最资讯丨打赢麦收“保卫战”
- 贵溪一化工厂硅油着火引发火情 去年因动火作业违规被罚 全球热消息
- 环球观天下!数据显示欧盟正在加紧推进今冬天然气储备
- 中国奥园(03883)公布2022年业绩 总营业额约187.11亿元 土地储备建筑面积约3083万平方米
- 深圳国际会展中心洲际酒店开业
- 焦点观察:新华社国家高端智库发布《改变中国的“第二个结合”——建设中华民族现代文明的理论创新与实践》智库报告
- 义乌脸基尼卖爆了,防晒装备销售火爆 全球今热点
- 小肚子大怎么减肥最快_小肚子大怎么减肥 天天精选
- 雷电+大风+强降雨临近!刚刚,内江发布最新预报—— 今日热搜
- 正荣服务(06958):邓历获任执行董事、行政总裁等职_世界微速讯
- 天天播报:《玉骨遥》定档,就在今晚,肖战任敏主演,编导都是商业剧高手
- 【世界聚看点】越南薇娅,中国人造出来了
- 黑芝麻智能赴港IPO,冲刺国内自动驾驶芯片第一股_世界通讯
- 打入点球获评全场最佳球员,王霜社媒:7月份迎来好的开始 全球实时
- 女人的贵气在手指上?
- 毕马威中国:金融科技由“模式创新”转向“技术创新”|全球实时
- 罗本内切动图_罗本内切|每日热闻
- 建筑央企重走长征路:汲取奋进力量用工匠精神建设精品工程 天天快播
- “实属罕见”!_每日焦点
- 卡车撞上多辆汽车:肯尼亚特大交通事故已致52人丧生
- 人形机器人核心部件!力传感器与编码器龙头20CM涨停,受益上市公司一览
- 环球关注:90%公寓楼和60%私人住宅被毁 俄罗斯人到重建中的马里乌波尔抄底买房
- 当前视讯!师姐毕业祝福语 师姐毕业祝福语简短七字
- 重庆中远海运物流有限公司_重庆中远国际货运有限公司相关内容简介介绍|速看料
- 【快播报】金毛犬多少钱一只在淘宝上(金毛犬多少钱一只)
- 【天天新视野】急性前列腺炎食疗方法都有哪些 昆山治疗前列腺炎的好医院
- 热门:65w小米11(3650的小米11u可以买吗)
- 儿童买火车票没有身份证怎么取票_中学生没有身份证怎么买火车票
- 国米希望从门将奥纳纳身上,盈利6000万欧元,预计曼联报价接近-焦点资讯
- 【独家焦点】省国土空间规划委员会第四次会议召开
- 天天日报丨岳阳市中医医院:坚持读书会活动 打造学习型医院
- APEC传染性疾病防控、治疗与应急处置能力建设论坛在昆明举办 独家
- 高圆圆贾静雯,敏若合体
- 华硕无畏Pro16 2023 旗舰版笔记本新配置上架
- 黑龙江宁安部分区域遭遇冰雹袭击 最大冰雹直径9厘米
- 头条焦点:江西贵溪一化工厂硅油着火引发火灾,当地消防:人员全部安全撤离
- 长行歌汉乐府的古诗(长行歌)
- 小米max3是什么屏幕材质|世界今热点
- 范玮琪发文为陈建州代写?友人称范范社交账号由丈夫打理
- hscrp高是什么原因引起_crp高是什么原因
-
本赛季18岁的居莱尔20场比赛中起脚射门34次打...|全球观天下
本赛季18岁的居莱尔20场比赛中起脚射门34次打进4粒进球以及完成3次助攻
-
脚丫君_脚丫论文网
1、挺不错的,这个网站不收定金的,是淘宝交易,很有保障,这个网站在
-
THS是啥意思_什么是THS 通讯
1、THS是打破的俚语。2、s语法:(1)是一个常用词,基本意思是突然施加
-
艘五笔怎么打_艘狐
1、就是搜狐想销售各种庙会东西如;豪侠包 商人包 小单等冲级用的东西
-
今日讯!南海观音菩萨像有多高 南海观音菩萨在哪里
1、普陀山南海观音坐落于双峰山南端的观音跳山岗上。2、此处势随峰起,
-
瑞斯康达:融资净偿还222.11万元,融资余额1.53亿元(06-30)-天天时讯
瑞斯康达融资融券信息显示,2023年6月30日融资净偿还万元;融资余额亿
-
书写人与自然和谐共生现代化的精彩篇章|生态文明贵阳国际论坛的前世今生
【开栏的话】多彩贵州,爽爽贵阳。2023年生态文明贵阳国际论坛,将于7
-
中红医疗(300981):6月30日北向资金减持10.33万股
6月30日北向资金减持10 33万股中红医疗。近5个交易日中,获北向资金增
-
6月30日基金净值:永赢消费主题A最新净值2.2573,涨0.04%_全球热推荐
6月30日,永赢消费主题A最新单位净值为2 2573元,累计净值为2 2573元,
-
文商旅组合发力 共促砚山民族团结|全球独家
近日,云南省文山壮族苗族自治州砚山县以举办云南省第十届盆景展为契机
-
Woj:在乌度卡到来之后 范弗里特成为火箭引援首选|环球简讯
Woj:在乌度卡到来之后范弗里特成为火箭引援首选,火箭,军训,woj,骑士团
-
校园图书角必备藏书:校园赠言_关于校园图书角必备藏书:校园赠言概略
1、《校园赠言》精选了多条受学生欢迎的名家赠言、美言警句,分为生活
-
环球微动态丨数码品质旗舰一加9R即将开售售价2999元起
科技、数码、互联网新闻如今都成为了大众所关注的热点了,因为在我们的
-
《青年说》第四十七期 他的第二故乡在南极
“跨越了极昼和极夜,从冰盖边缘挺进内陆腹地,从地表探索向南极航空迈
-
天天观察:工信部:1-5月我国规模以上互联网企业完成互联网业务收入5310亿元 同比增长2.8%
6月30日,工信部发布2023年1-5月份互联网和相关服务业运行情况。
-
湖北省公共卫生临床中心揭牌成立_环球热资讯
6月30日,从湖北省医防协同医防融合工作培训会上传来消息,湖北省公共
-
环球观天下!台州温岭:经营主体突破16万户
2023年5月底,谭林果在台州温岭开启新创业之旅——从重庆来到这里,他
-
辉瑞与石药集团宣布达成中国本土化新冠口服抗病毒治疗药物战略合作
6月29日,辉瑞公司与石药集团共同宣布,就中国本土化新冠口服抗病毒治
-
中科三环(000970.SZ):目前公司产品从毛坯到成品的收得率为65%左右
格隆汇6月30日丨有投资者向中科三环(000970 SZ)提问:公司产品的收得率
-
全球速递!人民币兑美元中间价调贬50个基点
人民币兑美元中间价报7 2258,调贬50个基点。前一交易日中间价报7 2208
-
农业银行社保卡密码锁定状态怎么办_世界热点评
如果你的社保卡连续三次输入错误,密码被锁定,系统不会自动解锁。那么
-
今日南财市场情绪指数为50.3,市场投资热度提升 速递
今日南财市场情绪指数为50 3,较前一交易日47 3上涨,市场投资热度提升
-
每日热门:在野外偶遇大熊猫是种什么体验
00:396月29日,陕西宝鸡太白县水利局工作人员下乡途中,在黄柏塬镇至高
-
WTCR又夺冠了,领克:无敌,是多么寂寞? 每日播报
对于房车赛,可能大家下意识会想到的是本田、奥迪这样的国际巨头,然而
-
2023甘肃省博物馆招聘空调管理员+电梯操作员
甘肃省博物馆招聘公告一、中央空调系统专职操作管理员任职要求:1 负责
-
佛山:44.7%规上工业企业实施数改智转
近年来,佛山掀起了制造业数字化智能化转型的浪潮,坚定不移推进制造业
-
世界动态:天山股份拟增资27亿获宁夏赛马51%股权 接手宁夏建材水泥资产推进专业化整合
宁夏建材(600449 SH)和天山股份(000877 SZ)正在进行资产腾挪,将更
-
苏罗维金被查?被捕?佩斯科夫:建议问问国防部_当前要闻
中新网6月29日电当地时间28日,《莫斯科时报》援引俄国防部消息人士的
-
天天热头条丨经济日报:全球贸易复苏之路崎岖不平
最近一段时间,持续低迷的全球贸易数据似乎有复苏迹象。联合国贸易和发
-
苏罗维金大将疑被逮捕?多方回应 天天时讯
在瓦格纳“武装叛乱”后,俄罗斯特别军事行动联合部队副总指挥、俄空天
-
郑州工商学院官网(郑州华水大学是几本)
来为大家解答以上问题。郑州工商学院官网,郑州华水大学是几本这个很多
-
环球动态:吃什么长头发最快养生_吃什么长头发最快
1、用生姜榨汁,擦在头皮,来回擦,生姜有促进头发生长之功效。2、另选
-
一男子吹空调后发热咳痰误 竟是“军团菌肺炎”|最新资讯
一男子吹空调后发热咳痰误竟是“军团菌肺炎”---那么,军团菌肺炎到底
-
德琪医药-B(06996.HK):6月29日南向资金减持1.5万股_环球资讯
6月29日北向资金减持1 5万股德琪医药-B(06996 HK)。近5个交易日中,
-
复仇催眠动画 复仇催眠
1、A45AA84C9B8CC077B1389E09C04ED1080A1DC894这个你懂的吧?我就不多说了。2、
-
环球快看:湘潭市第二届不动产登记行业职业技能竞赛决赛举行
湘潭市第二届不动产登记行业职业技能竞赛决赛举行,湘潭市,分中心,职业
-
天天热议:不卷不行了?小鹏G6重磅上市,定价20.99万起太顶了!
6月29日,小鹏G6正式上市开售,共推出了5款车型,售价区间为20 99万-27
-
天天关注:特斯拉Model3升级!新“上车”的宁德时代M3P电池什么来头?
Model3基础款后轮驱动版的电量将从60kWh升级为66kWh,采用M3P新型磷酸
-
成片旧改“后半篇”如何推进?上海人大启动“两旧一村”改造专项监督 世界微速讯
本市成片旧改正进入下半场。市人大常委会将于9月份听取和审议市政府关
-
环球热消息:复星国际(00656.HK)6月29日耗资212万港元回购40万股
格隆汇6月29日丨复星国际(00656 HK)公告,6月29日耗资212万港元回购40
-
ST花王: 关于公司股票实施其他风险警示相关事项进展情况的提示性公告
ST花王:关于公司股票实施其他风险警示相关事项进展情况的提示性公告
-
党支部打造特色亮点措施_支部建设特色亮点
1、煤矿企业党支部要开展多种多样的活动,煤矿工人工作是比较枯燥和危
-
浦发银行毕节分行:银警联动开展反洗钱防诈骗宣传活动|全球视点
近日,浦发银行毕节分行联合毕节市公安局七星关分局朱昌派出所,在朱昌
-
播报:科城街道:推进“小散乱”排水整治工作
日前,盐南高新区科城街道建设和生态环境局召开“小散乱”排水整治工作
-
新笑傲江湖结局 新笑傲江湖结局简述
hello大家好,我是城乡经济网小晟来为大家解答以上问题,新笑傲江湖结
-
智能交通管理平台【NoTraffic 】完成了 5000 万美元B 轮融资,M&G Investments 领投-今日看点
近日,智能交通管理平台NoTraffic宣布完成了5000万美元B轮融资。本轮融
-
【全球独家】宝骏云朵外观细节官图发布 7 月正式上市
[爱卡汽车国内新车原创]宝骏官方近日公布了宝骏云朵车型外观细节图。新
-
多所高校停用微信支付
6月29日讯,近日,西北大学公告称,因腾讯公司微信支付将于7月1日起对
-
【环球时快讯】治理再升级 互联网广告怎么管
互联网广告乱象需规范,治理力度加大。用户投诉广告问题,维权困难成诱
-
朱凤瀚:汉字系统和青铜文化是带标识性的物质与精神文化遗存
在我所从事的学术研究领域,可以作为中华文明精神标识与文化精髓的,首
X 关闭
X 关闭