首页 安全防御正文

Java帝国之安全争斗

访客 安全防御 2022-04-10 640 2 JavaJaasJdbc

1.前言

在Java在帝国第三代国王的推动下,帝国为臣民提供了一个名字Java 认证和授权服务(Java Authentication Authorization Service,简称JAAS)在第四代国王的争夺下,JAAS成功进入JDK,成为标准包的一部分。

国王希望JAAS能够统一安全领域,如JDBC这引发了使用的狂潮,成为一个重要的基础设施,专门设置了一个新职位JAAS大臣任命自己的心腹来推动这件事。

但希望越大,失望就越大越大。除了几个利益相关的豪门望族不断摇旗呐喊外,臣民们还对他们大喊大叫JAAS不屑一顾,用的人不多。

2雷秀才

IO这一天,大臣们在家里闲着,带着忠诚的工作人员InputReader 出去微服私访,来到北京一家著名的酒馆,点了几道精致的菜和一壶酒。在吃饭之前,我看到邻桌的一生叹了口气。

IO大臣心里一动,就叫他过来聊聊。

原来这位书生是雷秀才,说是家乡赋税沉重,都没法活下去了,特意来京城 *** ,无奈不得其法,连门都进不去。

IO大臣好奇地问发生了什么事。

雷秀才说:“都是JAAS惹得祸。”

“JAAS?”

“是认证和授权!” 雷秀才看到对方不知道,有点失望。

“认证? 授权?”

“认证是为了确定你是谁,通常需要验证对方提供的用户名和密码。授权是为了确定你能做什么。例如,是否可以创建帐户、删除用户等。”

“呃呃,想起来了,为什么不用官方?JAAS,帝国的标准还是挺好的,比如JDBC。”

“你不知道老先生,JDBC标准自然不用说,但这个JAAS,唉,用起来很麻烦,大家都不愿意用。但是那个JAAS大臣们一点也不在乎,一直在疯狂地推广它JAAS,如果没有,我们将不得不纳重税,我们将无法生存。”

“这有点麻烦,你打算怎么办?” IO大臣先试探对方的套路。

雷秀才压低了声音:“说实话,我们家推出了一个新的认证和授权制度,叫做 *** ecurity,想托京城的大人献给陛下,把JAAS替换掉。”

“哦?!” IO大臣坐直了身体,这是一件大事!

3 *** ecurity

IO大臣和InputReader 一个新的机会来了!

之前和线程大臣打架,和XML大臣斗,和JDBC/JTA大臣们打来打去,杀来杀去,自己也占不到什么便宜。

这一次,也许我们可以把握安全领域!

InputReader问道: “你说说这个 *** ecurity有什么好处?”

“简单、灵活、易用!比JAAS好用多了!” 雷秀才说。

“太抽象了,来点干货。”

雷秀才突然警惕起来,只是喝酒,笑而不语。

IO大臣决定打开天窗说亮话: “说实话,我是当朝的IO大臣,你不用怕,我可以帮你演陛下。”

“啊?!” 雷秀才看起来很惊讶。出乎意料的是,他在这里遇到了当代成员。看来早上去庙里拜佛是对的。他迅速站起来敬礼: “失敬失敬!”

IO大臣说:“现在可以谈谈你的 *** ecurity了吧?”

雷秀才已经准备好从袖子里拿出两张满是代码的纸,呈现给他IO大臣和InputReader:

  • SubjectcurrentUser=SecurityUtils.getSubject();
  • UsernamePasswordTokentoken=newUsernamePasswordToken("liuxin","123456");
  • currentUser.login(token);
  • if(currentUser.hasRole("admin")){
  • logger.info("You'reAdministrator!");
  • }
  • if(currentUser.isPermitted("user:delete")){
  • logger.info("Youcandeleteanyusers!becareful!");
  • }
  • currentUser.logout();
  • (友情提示:代码可左右滑动)

    IO大臣戴上老花镜,拿着纸看了很久:“为什么这里叫你?Subject啊? 为什么不叫呢?User?”

    “回大人,这个Subject 是安全领域的一个术语,表示了所谓的‘主体’,它可以代表用户或程序( *** 爬虫等)。我家乡的人也觉得这个术语有点难理解,想用User这种说法很容易理解,但考虑到现在很多系统都有User为了避免冲突,这个概念仍然被称为Subject好了。”

    InputReader问道:“你那个login如果登录失败怎么办?”

    “事实上,这种 *** 会抛出异常,需要应用程序处理。我们提供了很多Exception类别处理各种情况,如账户未知( UnknownAccountException) ,密码不正确(IncorrectCredentialsException) ,账户已锁定(LockedAccountException) ,尝试次数太多(ExcessiveAttemptsException) 等等。“

    IO大臣说:“但是程序为用户提供错误的信息时,必须提供模糊的信息,不能被别有用心的人使用,对吧?”

    “是的,成年人,给用户看的错误消息一定要模糊,比如用户名或者密码不正确。” 雷秀才看到了IO大臣们开始深入思考,非常高兴。

    “这里可以判断一个用户有什么角色(Role),什么权限(Permission),这个角色和权限直接有什么关系?” InputStream继续问道。

    “这个比较简单,角色可以简单地认为是一些权限的 *** ,比如admin这个角色的权限可能是删除用户、查看用户、修改用户等viewer这个角色可能只检查用户的权限。”

    “那个user:delete又是什么意思?” IO大臣的眼睛像火炬 。

    根据以往的宫廷斗争经验,这些细节必须弄清楚,否则被别人抓住,在法庭上,武术部长表面上沉默,心已经鄙视你数千次,他们不能重复JTA大臣的错误。

    雷秀才道:“这是我们定义的权限符号规则,格式如下:资源:操作:实例,用两个冒号分开,例如:

    user:create:U001 表示用户资源实例U001进行create操作

    user:create 表示资源create操作,相当于user:create:*

    user:*:U001 表示所有操作用户资源实例01”

    IO大臣点了点头,格式由 *** ecurity但数据内容需要应用程序来确定。

    InputReader突然说:“记得大人提出Java安翰林注释了吗?如果是这样的话 *** ecurity支持注释。”

    雷秀才说:“支持支持,那个注释很好用。”

  • @RequiresAuthentication
  • publicvoidupdateAccount(AccountuserAccount){
  • ////用户认证后才能执行此 ***
  • ...
  • }
  • @RequiresPermissions("account:create")
  • publicvoidcreateAccount(Accountaccount){
  • ///用户必须具备account:create这个权限
  • ///执行此 ***
  • ...
  • }
  • @RequiresRoles("admin")
  • publicvoiddeleteUser(Useruser){
  • ///只有admin只有这个角色的用户才能执行这种 ***
  • ...
  • }
  • IO大臣觉得这里耳目众多,不宜长时间留下,建议回自己家继续商量。

    4Realm

    三人回到IO大臣府还没等茶,InputReader焦急地问道:“你的代码看起来很简单,只是 *** ecurity在哪里验证这些用户名、密码、权限和角色?”

    看到问题越来越深,雷秀才也越来越高兴,看来今天真的遇到了贵人。

    “这真是个好问题,大人,” 雷秀才说,“对于每个应用程序来说,这些与安全相关的数据保存的地方可能不同,可能在文本文件、数据库或LDAP服务器中...... 数据格式不同,有些用户被称为user,有些可能被称为username,有些人称密码为密码password,有些可能叫pwd...... 考虑到我们 *** ecurity这是一个框架,必须有一个抽象的概念,这个概念被称为Realm ,听起来有点奇怪。”

    雷秀才不好意思笑了笑,继续往下说:“这个Realm 是一个接口,就像一座桥,将应用程序的具体数据和我们一起 *** ecurity连接框架可理解的格式! 它可以将用户应用的独特安全数据转化为 *** ecurity能理解的格式。”

    “ 是否每个应用程序都必须提供一个独特的JDBCRealm/LDAPRealm/IniRealm这样的实现类吗?” IO大臣们表示不满。

    “不不,”雷秀才赶紧救火,“我们的 *** ecurity框架提供了这些缺陷的实现。使用时只需稍加调整即可。例如,成年人有一个用户名和密码存储在数据库表中的应用程序,usesr(id ,name,pwd......),你只需要提供一个sql 给JDBCRealm,我们的框架可以自动完成认证。”

    雷秀才又抛出了一张图。

    InputReader 看着这张图,整个认证过程自动补脑:

    1. 使用应用配置JDBCRealm (当然要提供数据库的连接信息)

    2. 应用告诉 *** ecurity 如何从用户表中获取用户名?password,关键是那条sql:

    jdbcRealm.authenticationQuery = select pwd from users where name= ?

    3. 用户执行subject.login操作, *** ecurity 使用SQL查询用户名,密码是否与数据库值相匹配。

    (注: 简单起见,故意忽略使用salt对密码做hash的场景)

    类似的角色和权限也可以提供sql ,让 *** eurity从数据库表中获取相关数据:

    jdbcRealm.userRolesQuery = "SELECT role_name FROM user_roles WHERE user_name = ?"

    jdbcRealm.permissionsQuery = "SELECT permission FROM roles_permissions WHERE role_name = ?"

    “如果一个应用程序配置多个应用程序Realm 认证时该怎么办?” InputReader继续刨根问底。

    雷秀才暗暗佩服InputReader说:“我们定义了一个叫做接口的接口AuthenticationStrategy,用来定义认证多个Realm我们还提供了们也提供了几个默认的实现,比如FirstSuccessfulStrategy,遇见一个Realm成功的认证是成功的;或者AllSuccessfulStrategy,必须所有的Realm都认证成功。”

    InputReader点点头,他们似乎想得很仔细。显然,类似的策略也可以定义为授权。

    雷秀才画了一张展示认证和授权结构的图片:

    5Session管理

    “嗯,我认为你在认证和授权方面做得很好!” IO大臣们试图总结。

    “大人,我们也支持一些诱人的功能。Session管理。”

    “Session 那不是Tomcat之类的 Web Container要做什么?” InputReader 问道。

    “是的,所以一般来说,你想用它Session,必须有个像Tomcat,Jetty这样的Web Container但是如果你使用我们的 *** ecurity,一点也不用Tomcat,Jetty,我们对Session内置支持,也就是说,即使是桌面应用也可以使用Session:”

    Subject currentUser = SecurityUtils.getSubject();

    Session session = currentUser.getSession();

    session.setAttribute( "someKey",someValue);

    “这是个不错的卖点!” InputReader 对IO大臣使眼色。

    “还有哪些功能?” IO大臣胃口不小。

    “我们还提供了一些可以加密的工具。当然,我们还是对的Web发展提供了强有力的支持。”

    “大人,属下觉得这个API设计真的很简单,比那个简单JAAS清爽多了”。InputReader对IO大臣说道。

    6尾声

    IO他铿锵有力地说:“ 我们陛下是一代圣君,但却是JAAS这些大臣给蒙蔽了,这样下去,民不聊生,Java帝国就要死了,明天老夫就去参它一本!”

    雷秀才看到当代大员愿意为自己出头,感动得无法复加。

    可是InputReader拉过IO大臣悄悄地说:“大人,这个JAAS经过两代国王的努力才进入JDK,它充分代表了富裕家庭的利益JAAS大臣是国王身边的红人,不能说废就废。如果你想这样玩,你必须碰钉子,你必须曲线救国。”

    “曲线救国?”

    “下属建议先让这个 *** ecurity 开源,让它加入著名的民间组织Apache,让臣民先用,让我们秘密补贴,这么好的东西肯定会形成气候,直到火灾燎原,我们的陛下不得不让步,然后JAAS大臣估计会倒台。”

    IO大臣点头赞许。

    第二天,雷秀才被送往Apache ,在那里 *** eurity被改名为Shiro,开始向民间传播。

    果然,几年后,越来越多的人喜欢它Shiro,JAAS被冷落,国王见状,不得不让步JAAS大臣回家养老。

    【本文为51CTO专栏作者“刘欣”请通过作者微信微信官方账号转载原稿。coderising获取授权】

    戳这里,看作者更好的文章

       
    版权声明

    本文仅代表作者观点,不代表本站立场。
    本文系作者授权发表,未经许可,不得转载。