范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

casbin权限模型推演

  无论什么项目只要涉及到多个用户的操作都会开始考虑权限控制, 权限管理是一个很常见部分,所以出现了单独处理这个部分的开源项目,即本文要介绍的casbin项目。
  casbin支持很多的编程语言, 本文选择golang作为使用语言。 认证还是授权?
  在大型项目中认证(Authentication)和授权(Authorization)一般是分开的,前者用于甄别用户是谁, 而后者用于判断用户有什么权限,授权不管认证, 认证也不管授权, 这点很重要, 但是很多时候为了简单会将两者放在一起,比如仅是判断用户是否认证,认证了就可以访问该资源,而本文主要讨论授权的问题, 所以不会关心认证的问题, 即没有验证用户名密码是否认证通过的业务逻辑。 准入
  权限最开始总是准入,即只有两个选择,允许或者拒绝。
  比如这样的场景,存在四个用户,zhangsan, lisi, wangwu, zhaoliu, 我们允许前三人可以访问我们的网站。
  所以我们将策略描述如下. zhangsan lisi wangwu
  在这个列表中的用户表示允许,反之不允许。 资源控制
  随着项目的增长我们自然发现用户可以操作的项目多起来了, 所以需要在之前的策略里加上用户对应的资源。
  比如这样的场景, 还是之前那四个用户,但是我们的网站变成了2个, 我们允许张三访问两者,后三人只能访问第二个网站。
  所以我们将策略描述如下. zhangsan, web1 zhangsan, web2 lisi, web2 wangwu, web2 zhaoliu, web2
  还是一样的逻辑,只是多了一个字段
  我们可以将第一列称为user,第二列称为website。
  与此同时,我们还需要对用户有更精细的控制,比如张三可以读写所有网站,但是其他三个人只读第二个网站,所以策略可以描述如下。 zhangsan, web1, read zhangsan, web1, write zhangsan, web2, read zhangsan, web2, write lisi, web2, read wangwu, web2, read zhaoliu, web2, read
  我们将第三列称为action
  至此我们基本上完成了权限控制,但是稍稍有点不完美, 这里的不完美是策略文件跟匹配模型太耦合, 比如我们还是上面的策略文件,但是网站变成了10个,并且我们希望只要在列表中的用户就能访问所有网站,又或者不管第二个字段,只根据第三个字段来判断用户的可读可写权限,最简单直接的办法自然是直接重写一遍策略文件并相应的修改代码,但是太无聊太枯燥了,所以我们需要将其 模型 提炼出来。
  比如我们可以定义一个这样的模型 # 策略定义的意思 [policy_definition] p = user, website, action  # 匹配逻辑 [matchers] m = user == p.user && website == website && action == action
  这样我们就可以解决之前的问题了
  比如只匹配第一个字段, 那我们可以定义如下 # 策略定义的意思 [policy_definition] p = user, website, action  # 匹配逻辑 [matchers] m = user == p.user
  又比如只匹配第一个和第三个字段 # 策略定义的意思 [policy_definition] p = user, website, action  # 匹配逻辑 [matchers] m = user == p.user && action == p.action
  至此我们可以在不改动策略文件的情况下仅仅改变比较小的内容就可以很快的完成匹配模型的转换,这样就会灵活很多,但是现在的模型还有些不太严谨, 我们通过 p = user, website, action  定义了策略文件的各个字段,却没有定义用户请求的各个字段, 比如要求用户请求应该填上哪些字段,所以我们需要再次改一下我们的匹配模型,修改如下: # 请求定义的意思 [request_definition] r = user, website, action  # 策略定义的意思 [policy_definition] p = user, website, action  # 匹配逻辑 [matchers] m = r.user == p.user && r.action == p.action
  这样子我们的匹配模型看起来要严谨许多了,但是模型中请求定义(request_definition), 策略定义(policy_definition)在toml中的语法其实都是列表, 即我们可以定义多个策略和请求定义,比如: # 请求定义的意思 [request_definition] r = user, website, action r2 = user, action  # 策略定义的意思 [policy_definition] p = user, website, action p2 = user, action  # 匹配逻辑 [matchers] m = r.user == p.user && r.website == p.website && r.action == p.action m2 = r2.user == p2.user && r2.action == p2.action m3 = r.user == p.user && r.action == p.action
  这样我们可以在一套模型中定义多个不同的组合,比如(r,p,m), (r2,p2,m2), (r,p,m3), 总的来说我们的匹配模型灵活性大大提高,但是我们的策略模型可能出现了不严谨的地方,即策略文件中的每一行是策略p, 还是策略p2? 我们无法判断,所以为了解决这个问题,我们需要在策略文件中多加一个字段.
  策略文件定义如下: p, zhangsan, web1, read p, zhangsan, web1, write p, zhangsan, web2, read p, zhangsan, web2, write p, lisi, web2, read p, wangwu, web2, read p, zhaoliu, web2, read p2, sunqi, read
  可以看到,我们增加了一个用户sunqi, 他不需要定义website,因为策略p2不需要website这个字段。
  现在我们稍稍将名称再提炼一下,假设我们多了程序接口,也就是说使用者不是用户而是终端,我们可以称其为用户,但是稍稍有些别扭,我们可以将其统称为主体(subject),我们的项目也不可能总是网站,所以我们可以称其为对象(object), 而操作权限我们可以归纳为动作(action).
  所以仅仅是为了让我们的模型的语言看起来更加的泛化,所以我们将其改成如下 # 请求定义的意思 [request_definition] r = subject, object, action r2 = subject, action  # 策略定义的意思 [policy_definition] p = subject, object, action p2 = subject, action  # 匹配逻辑 [matchers] m = r.subject == p.subject && r.object == p.object && r.action == p.action m2 = r2.subject == p2.subject && r2.action == p2.action m3 = r.subject == p.subject && r.action == p.action m3 = r.subject == p.subject && r.action == p.action
  这个时候又来了新的需求,即再多加一个用户(zhouba),只需要这个用户不能访问web10(假设已经有10个网站。)即可,一种做法是为这个用户添加18条记录,即web1,web2,...,we9, 分别对应read和write, 作为一个程序员自然是讨厌这些枯燥无聊的工作的。
  所以我们可以继续改进我们的匹配模型, 我们发现我们的匹配模型对于结果的判断过于单一,即只能允许,我们无法在已有的框架下扩展,这其实是因为我们没有处理匹配的结果,我们应该对匹配的结果进一步做处理,我们可以将匹配的结果称之为result, 而result有允许和拒绝(deny)两种, 在这个结果下,我们可以定义这样的语法,匹配到任意一个允许(allow)就放行,又或者没有任何一个拒绝(deny)就放行,而后者就是我们想要的解决方案,这样我们只要写一条拒绝访问web10的规则就可以达到目的。
  所以模型定义如下: # 请求定义的意思 [request_definition] r = subject, object, action r2 = subject, action  # 策略定义的意思 [policy_definition] p = subject, object, action p2 = subject, action p3 = subject, object, result  # 策略结果的意思 [policy_result] e = some(where (p.result == allow)) e2 = !some(where (p.result == deny))   # 匹配逻辑 [matchers] m = r.subject == p.subject && r.object == p.object && r.action == p.action m2 = r2.subject == p2.subject && r2.action == p2.action m3 = r.subject == p.subject && r.action == p.action m3 = r.subject == p.subject && r.action == p.action m4 = r.subject == p.subject && r.object == p3.object
  这里我们多定义了一个段落policy_result, 这个段落有两个执行逻辑,前者代表只要有一行的策略匹配结果是allow就放行,后者是没有一行的策略匹配结果是deny就放行。
  为啥语法要定义成这样? 因为这是casbin的语法, 我只是拙劣的模仿,并且按照自己的理解来一步步推到casbin模型...语法只是一套要记住的规则而已,如果我们不需要自己解析的话,死记硬背即可,当然了,它的这个语法也不是难以理解的那种。
  而新的策略规则如下: p, zhangsan, web1, read p, zhangsan, web1, write p, zhangsan, web2, read p, zhangsan, web2, write p, lisi, web2, read p, wangwu, web2, read p, zhaoliu, web2, read p2, sunqi, read p3, sunqi, web10, deny
  至此整个模型基本完成了,可以适配大多数的访问控制(ACL)情况了,但是对于RBAC还是有些问题,但是这里就不继续演进了。后面通过代码来看看ACL, RBAC的是用。
  当然了,你可能觉得模型的演进还是有很多问题,比如casbin使用的是简写sub,而这里使用的是subject全称,这里策略效果写的段落名是policy_result,而casbin写的是policy_effect, 不过这些不同之处在我看来只是小问题,只需替换即可。 代码示例
  这一节直接使用golang来演示。 ACL
  假设场景: 存在网站web1,web2, 张三可读写两者,李四只读web1。
  模型定义如下: [request_definition] r = sub, obj, act  [policy_definition] p = sub, obj, act  [policy_effect] e = some(where (p.eft == allow))  [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
  策略定义如下: p, zhangsan, web1, read p, zhangsan, web1, read p, zhangsan, web2, read p, zhangsan, web2, write p, lisi, web1, read
  代码如下: package acl1  import ( 	"log" 	"testing"  	"github.com/stretchr/testify/assert"  	"github.com/casbin/casbin/v2" )  func TestACL1(t *testing.T) { 	e, err := casbin.NewEnforcer("model.conf", "policy.csv") 	if err != nil { 		log.Fatal("创建策略引擎失败: ", err) 	} 	tests := [][]interface{}{ 		{"zhangsan", "web1", "read"}, 		{"zhangsan", "web1", "write"}, 		{"zhangsan", "web2", "read"}, 		{"zhangsan", "web2", "write"}, 		{"zhangsan", "webx", "write"}, 		{"lisi", "web1", "read"}, 		{"lisi", "web2", "read"}, 		{"lisi", "webx", "read"}, 	} 	expected := []bool{true, true, true, true, false, true, false, false}  	for i := 0; i < len(tests); i++ { 		ok, err := e.Enforce(tests[i]...) 		if err != nil { 			t.Fatalf("请求: %v 对应的期待是是: %t, 发生错误: %s", tests[i], expected[i], err) 		} 		assert.Equal(t, expected[i], ok) 	} }
  在此基础上我们需要一个超级管理员,就称其为root
  所以模型如下: [request_definition] r = sub, obj, act  [policy_definition] p = sub, obj, act  [policy_effect] e = some(where (p.eft == allow))  [matchers] # 唯一的不同是是加了|| p.sub == "root", 只要用户名是root就允许 m = r.sub == p.sub && r.obj == p.obj && r.act == p.act || p.sub == "root"
  策略文件不变。
  测试代码如下 package acl1  import ( 	"fmt" 	"log" 	"testing"  	"github.com/stretchr/testify/assert"  	"github.com/casbin/casbin/v2" )  func TestACL2(t *testing.T) { 	e, err := casbin.NewEnforcer("model.conf", "policy.csv") 	if err != nil { 		log.Fatal("创建策略引擎失败: ", err) 	} 	tests := [][]interface{}{ 		{"zhangsan", "web1", "read"}, 		{"zhangsan", "web1", "write"}, 		{"zhangsan", "web2", "read"}, 		{"zhangsan", "web2", "write"}, 		{"zhangsan", "webx", "write"}, 		{"lisi", "web1", "read"}, 		{"lisi", "web2", "read"}, 		{"lisi", "webx", "read"}, 		{"root", "web1", "read"}, 		{"root", "webx", "update"}, 	} 	expected := []bool{true, true, true, true, false, true, false, false, true, true}  	for i := 0; i < len(tests); i++ { 		ok, err := e.Enforce(tests[i]...) 		fmt.Println(tests[i], expected[i]) 		if err != nil { 			t.Fatalf("请求: %v 对应的期待是是: %t, 发生错误: %s", tests[i], expected[i], err) 		} 		assert.Equal(t, expected[i], ok) 	} }
  测试结果也是通过的,可以看到root的测试用例中即使请求不存在的资源或者不存在的操作也是true, 因为模型中只判断用户是否为root。 RBAC
  假设场景: 存在网站web1,web2, 可读角色(reader)可读写两个web,可读角色(writer)可写两个web,管理员角色(admin)可读写两者, 张三属于可读角色,李四属于可写角色,王五属于admin角色,赵六既属于可读角色也属于可写角色。
  模型描述如下: [request_definition] r = sub, obj, act  [policy_definition] p = sub, obj, act  [role_definition] g = _, _  [policy_effect] e = some(where (p.eft == allow))  [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
  RBAC与ACL的不同之处在于多了一个role_definition, 多了一层抽象自然需要一个新的定义,这没什么奇怪的,就像request_definition, policy_definition. 不过它的语法又稍稍不同,首先它不用sub, obj之类的对象名称,仅用"_"作为所需参数的占位符, 两个下划线说明需要两个参数。
  比较难以理解的是 g(r.sub, p.sub)  , g是role_definition定义的一个角色操作符, 但是这个需要对照策略文件查看。
  策略文件如下: # 策略定义 p, reader, web1, read p, reader, web2, read p, writer, web1, write p, writer, web2, write p, admin, web1, read p, admin, web1, write p, admin, web2, read p, admin, web2, write # 定义用户属于哪些角色 g, zhangsan, reader g, lisi, writer g, wangwu, admin g, zhaoliu, reader g, zhaoliu, writer
  策略文件中分为两个部分,第一部分属于常见策略定义,不过这里定义的主体(sub)是后面定义的角色,即角色绑定到了具体的对象及操作,而g定义了用户属于哪些角色,比如 g, zhangsan, reader  代表zhangsan属于可读角色(reader),而可读角色(reader)可以读写web1, web2, 从来可以推导出zhangsan可读web1,web2。
  在回过头看 g(r.sub, p.sub)  我们可以理解为g操作符将 r.sub  映射成了对应的角色,再将其角色与 p.sub  比较。因为请求中没有角色的数据,所以必然需要一个映射函数将其转换成对应的角色,casbin使用的角色定义的 g 。
  测试代码如下: package acl1  import ( 	"fmt" 	"log" 	"testing"  	"github.com/stretchr/testify/assert"  	"github.com/casbin/casbin/v2" )  func TestRBAC(t *testing.T) { 	e, err := casbin.NewEnforcer("model.conf", "policy.csv") 	if err != nil { 		log.Fatal("创建策略引擎失败: ", err) 	} 	tests := [][]interface{}{ 		{"zhangsan", "web1", "read", true}, 		{"zhangsan", "web2", "read", true}, 		{"zhangsan", "web1", "write", false}, 		{"lisi", "web1", "write", true}, 		{"lisi", "web2", "write", true}, 		{"lisi", "web1", "read", false}, 		{"wangwu", "web1", "read", true}, 		{"wangwu", "web1", "read", true}, 		{"zhaoliu", "web1", "read", true}, 		{"zhaoliu", "web2", "write", true}, 	}  	for i := 0; i < len(tests); i++ { 		ok, err := e.Enforce(tests[i][:3]...) 		fmt.Println(tests[i], tests[i][3]) 		if err != nil { 			t.Fatalf("请求: %v 对应的期待是是: %t, 发生错误: %s", tests[i], tests[i][3], err) 		} 		assert.Equal(t, tests[i][3], ok) 	} }
  测试自然是成功的。
  值得注意的是: 虽然策略里面定义了角色的权限,但是也可以定义用户的权限,比如加一行 p, zhangsan, web1, write  , 可也是可以的,但是初学起来觉得奇怪。熟悉之后可以任意的测试和组合。 上下文切换
  在之前的模型推导过程中,模型总是定义了不止一个策略,不止一个匹配器,那么怎么在代码中体现呢?
  模型定义如下: [request_definition] r = sub, obj, act r2 = sub, obj  [policy_definition] p = sub, obj, act p2 = sub, obj  [policy_effect] e = some(where (p.eft == allow)) e2 = some(where (p.eft == allow))  [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act m2 = r2.sub == p2.sub && r2.obj == p2.obj
  会发现每个对象都多了一份, 比如r2的定义说明只需要两个参数,而r需要三个参数,其他意思差不多。
  策略定义如下: # 策略定义 p, zhangsan, web1, read p, zhangsan, web2, read p2, wangwu, web1 p2, wangwu, web2
  分别为策略p, p2定义不同的策略,需要的参数不同
  测试代码如下: package acl1  import ( 	"log" 	"testing"  	"github.com/stretchr/testify/assert"  	"github.com/casbin/casbin/v2" )  func TestRBAC(t *testing.T) { 	e, err := casbin.NewEnforcer("model.conf", "policy.csv") 	if err != nil { 		log.Fatal("创建策略引擎失败: ", err) 	} 	tests1 := [][]interface{}{ 		{"zhangsan", "web1", "read", true}, 		{"zhangsan", "web2", "read", true}, 		{"zhangsan", "web1", "write", false}, 		{"wangwu", "web1", "write", false}, 		{"wangwu", "web2", "write", false}, 	}  	ctx2 := casbin.NewEnforceContext("2")  	tests2 := [][]interface{}{ 		{ctx2, "wangwu", "web1", true}, 		{ctx2, "wangwu", "web2", true}, 		{ctx2, "wangwu", "web3", false}, 	}  	for i := 0; i < len(tests1); i++ { 		ok, err := e.Enforce(tests1[i][:3]...) 		// t.Log(tests1[i], tests1[i][3]) 		if err != nil { 			t.Fatalf("请求: %v 对应的期待是是: %t, 发生错误: %s", tests1[i], tests1[i][3], err) 		} 		assert.Equal(t, tests1[i][3], ok) 	}  	for i := 0; i < len(tests2); i++ { 		ok, err := e.Enforce(tests2[i][:3]...) 		// t.Log(tests2[i], tests2[i][3]) 		if err != nil { 			t.Fatalf("请求: %v 对应的期待是是: %t, 发生错误: %s", tests2[i], tests2[i][3], err) 		} 		assert.Equal(t, tests2[i][3], ok) 	} }
  这与之前的不同在于第一个参数是context,这里为了简单没有单独的设置各个部分的值,比如这里的 casbin.NewEnforceContext("2")  说明使用(r2,p2,e2,m2), 但是e2跟e分明是一样的,所以可以单独设置context的EType为 "e" , 这里就不展开了… 一些额外的技巧
  一些常使用的技巧 黑名单策略
  本文全篇都是白名单策略,即允许才放行,但是有时候很名单更有效,比如网站的反爬策略,大多数链接都是允许的,只有一部分是不允许的, 所以用白名单去放行所有资源显然有点不现实及不高效,所以我们可以将策略结果进行如下设置 [policy_effect] e = !some(where (p.eft == deny))
  该语法声明的是,不存在任何决策结果为 deny  的匹配规则,则最终决策结果为 allow
  但是什么时候 p.eft == deny  呢? 其实策略定义中可配置eft这个属性,定义如下 [policy_definition] p = sub, obj, act, eft
  然后对应的策略定义如下: p, zhangsan, web1, read, allow p, zhangsan, web2, read, deny数据源适配
  一般来说模型文件是放在本地,并且很少更改,而策略文件可以放在很多地方,比如数据库,代码如下。 import (     "log"      "github.com/casbin/casbin/v2"     "github.com/casbin/casbin/v2/model"     xormadapter "github.com/casbin/xorm-adapter/v2"     _ "github.com/go-sql-driver/mysql" )  // 使用MySQL数据库初始化一个Xorm适配器 a, err := xormadapter.NewAdapter("mysql", "mysql_username:mysql_password@tcp(127.0.0.1:3306)/casbin") if err != nil {     log.Fatalf("error: adapter: %s", err) }  m, err := model.NewModelFromString(` [request_definition] r = sub, obj, act  [policy_definition] p = sub, obj, act  [policy_effect] e = some(where (p.eft == allow))  [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act `) if err != nil {     log.Fatalf("error: model: %s", err) }  e, err := casbin.NewEnforcer(m, a) if err != nil {     log.Fatalf("error: enforcer: %s", err) }
  代码摘自: https://casbin.org/zh/docs/get-started casbin编辑器
  在线地址: https://casbin.org/zh/editor
  在线编辑器虽然可以很方便的验证想法,但是使用稍稍有些限制,只不过对于大多数人不是问题,因为不需要上下文切换。 总结
  自己写一个ACL或者RBAC倒是不太复杂,但是枯燥无味就像写CRUD一样并且不够灵活,而Casbin是比较强大的,它支持超级多的模型,ACL, RBAC, ABAC等多种策略模型。

安徽六安警方发布警情通报两车高速碰撞起火一人轻伤央广网合肥9月21日消息(记者徐鹏)9月21日中午,安徽省六安市公安局交警支队发布警情通报2022年9月21日7时许,G42沪蓉高速金寨往武汉方向船板冲大桥处,胡某中驾驶载有硅油(终于,她宣布离婚,全网恭喜我一定会离婚,只是时间早晚的问题。选择自驾游离家2年的苏敏,再回到家时,平静地做出这个决定。前几天,在孩子们的劝说下,她于中秋节前返程,回到郑州的家。在分离的2年里,丈夫仅仅跟她通蒲巴甲的火辣情史,和他著名的前女友们进错花田的那位哥出事之后,蒲巴甲赶紧在社交平台发表紧急自我介绍,说他叫做蒲巴甲。王力宏蒲巴甲李治廷三人因为长相极其相似,被网友戏称为大哥,二哥和三哥。蒲巴甲刚出道的时候,也总是拿着巴洛贡目前在兰斯陷入了困境,但这正是我想要的磨炼直播吧9月21日讯巴洛贡接受了独立报的采访,在采访中他承认自己在兰斯陷入了困境,他现在依然在试图克服语言障碍以及适应法甲。巴洛贡在本赛季从阿森纳租借加盟兰斯,他在六场首发的比赛中贡江苏泰兴梅香两岸促同心京韵传情一家亲活动合影(图片来源泰州台办)中国台湾网9月21日讯凤仪城,舞霓裳,梅也芬芳兰也香。9月20日下午,梅香两岸?京韵传情两岸青少年京剧文化线上交流会在京剧文化特色学校泰兴市鼓楼小学教育国美过冬,黄光裕出走花朵财经原创话说今年以来,黄光裕已多次减持国美的股份。为何?要知道,黄光裕身为国美的创始人,在去年出狱时,才刚喊出了力争用未来18个月的时间,使企业恢复原有的市场地位。结果这会国美奇迹世界经典8月份将揭开未来更新计划内容作为一款全球比较火爆PCMMORPG游戏奇迹世界经典2022年8月份揭开未来更新内容,我们一起了解一下吧!未来更新内容包括有新地区,还有三款野外地图,二款新副本,增加第六代装备,等自由之刃评测经典动漫改编MT挂机养成手游自由之刃是由经典动漫改编的MT题材挂机养成手游。游戏以原著动漫剧情为主线,融入主流手游养成玩法,采用回合制战斗玩法打造的一款全新养成挂机手游,为大家带来一款全新的MT冒险之旅。菊爆中超长春亚泰vs武汉三镇作为升班马的武汉三镇实力冠绝中超?9月20日1730长春亚泰受疫情影响长春亚泰队已经将近20天没有比赛,只踢了15轮的长春亚泰队,上轮比赛01负于成都蓉城。目前15战之后4胜7平4负,暂时排名联赛第11位。本场比赛女篮世界杯实力榜更新!中国跌至第三,12人大名单呼之欲出北京时间9月20日,男篮欧洲杯已经落下帷幕,接下来女篮世界杯即将开打,这次中国女篮姑娘们也将亮相,将向好的名次冲击。在开打之前,FIBA官方也更新了女篮世界杯的实力榜,美国占据着第葬礼百态!拜登吊唁打小抄,马克龙穿跑鞋被批,特鲁多高歌狂想曲文徐天民工作室李不言英国女王的葬礼可谓隆重,邀请了多国政要参与,其中更是有近百位各国的领导人出席。然而在这场葬礼期间,美国总统拜登吊唁打小抄,法国总统马克龙穿跑鞋被批,加拿大总理特
国务院26个新部委领导,其中一位是上将军衔副国级,曾被美国制裁3月20日国务院发布了关于机构设置的通知,新组成部委26个,其中新任命的国防部部长李尚福是陆军上将军衔,今年两会期间被任命为国务委员,副国级干部。令人惊讶的是李尚福上任不到24小时倒水太满就能辞退试用期员工?资料照片3月13日,山西太原女子小亚在社交平台上反映她入职一家公司,因给客人倒茶水太满,被公司开除。小亚2月23日入职公司,岗位是文秘,有六个月的试用期。3月9日下午公司会议,她正下周超千亿市值解禁来袭,ChatGPT人气股解禁比例超5成,中字头制造龙头解禁市值近500亿财联社3月25日讯(编辑魏齐)公开数据显示,下周(3月27日3月31日)沪深两市共有65家公司限售股陆续解禁,合计实际解禁量71。26亿股,按最新收盘价计算,解禁市值为1171。1社保最新持仓数据减持绩优股,看好这一板块!随着年报的密集披露,前一季度社保基金的持仓变动也逐渐浮出水面。通联数据显示,截至3月24日2022年四季度末,社保基金共现身62只个股前十大流通股股东榜单。其中,新进9只,增持28西班牙狂欢节必须哭着结束来源环球时报环球时报驻西班牙特约记者王方狂欢节是西班牙人的重要节日之一,又称化妆节,参加游行的民众纷纷别出心裁地化妆后粉墨登场。西班牙狂欢节每年2月下旬开始,为期一周,今年2月17曾国藩出身平常的小镇青年,如何成长为大清王朝的最后领航者?曾国藩,一个小地方出身的普通人,是如何成为晚清四大名臣?说起中国历史上著名的政治家,不得不提曾国藩。他声名远扬,被誉为中国三个半圣人中的半圣,文能提笔安天下,武能马上定乾坤。他功勋仰视人生岁月如茶,简简一杯最近很喜欢的一段话忘记年龄,讨好自己,用自己喜欢的方式生活。简单点糊涂点开心点,一切都刚刚好。一壶茶,带着岁月的暖香,因为时光的珍存,才有着无量的茶味,岁月里,都飘满了茶味。岁月如李雪主陪同金正恩乘专机出访,气质端庄优雅,尽显第一夫人风范李雪主拿着时尚小包,陪同丈夫金正恩站在红地毯上,很快他们就要乘坐专机出访,三军仪仗队已经做好了准备,金正恩与夫人李雪主,迈着坚定的步伐走在红地毯上,李雪主气质端庄优雅,尽显第一夫人陕西一名酒再次崛起,因碰瓷五粮液惨背黑锅,如今强势归来说起白酒的由来,就要追溯到千年以前了。到了现在,几乎各地都有上几款代表美酒,可以看出我国国人对酒的喜爱了。在众多白酒中,最受追捧的还属大牌,也就是我们现在所说的明星酒了。因为品牌效科技前沿我科学家研发出可穿戴人工喉还原准确率超90科技前沿一枚硬币大小的石墨烯片,贴在颈部靠近喉咙处,就能帮助发音障碍者获得新声。近日,清华大学集成电路学院任天令教授及合作团队在智能语音交互方面取得重要进展,其研发的可穿戴人工喉可安易数据恢复软件下载使用方法目前在网上我们可以找到很多不同类型的数据恢复软件,不同的软件功能和特点都不同。有些网友对安易数据恢复软件感兴趣,不知道具体怎么下载使用安易数据恢复软件。下面小编就给大家分享下详细的