当嵌套在LockoutRealm中时,如何在Java内部获取Tomcat CredentialHandler

当嵌套在LockoutRealm中时,如何在Java内部获取Tomcat CredentialHandler,java,tomcat,Java,Tomcat,我使用的是Tomcat 8.5.59,在context.xml中有以下领域: 我想让我的Java应用程序中的CredentialHandler散列并存储密码。根据克里斯托弗·舒尔茨(Christopher Schultz)在会议上的发言,我得到如下结论: CredentialHandler ch=(CredentialHandler)application.getAttribute(Globals.CREDENTIAL\u HANDLER); 问题是它返回一个空白/默认凭证处理程序,而不是

我使用的是Tomcat 8.5.59,在context.xml中有以下领域:


我想让我的Java应用程序中的CredentialHandler散列并存储密码。根据克里斯托弗·舒尔茨(Christopher Schultz)在会议上的发言,我得到如下结论:

CredentialHandler ch=(CredentialHandler)application.getAttribute(Globals.CREDENTIAL\u HANDLER);
问题是它返回一个空白/默认凭证处理程序,而不是我配置的那个

如果我删除了LockoutRealm定义,它工作得很好,因此似乎有一个嵌套的领域(LockoutRealm,DataSourceRealm)会导致它失败。查看Tomcat代码,调用
setAttribute(Globals.CREDENTIAL\u HANDLER)
的代码似乎没有考虑到
CombinedRealm

如何在应用程序中获取已配置的凭据处理程序,以便调用.matches()和.mutate()方法? 我宁愿不删除锁定域,因为这会损害安全性


编辑:

这个用例很常见:应用程序必须能够将经过修改的密码保存到数据库中,以便tomcat可以在用户登录时对其进行基于表单的身份验证。每次创建一个新的用户帐户时,必须修改用户的密码并将其保存到数据库中

此外,当用户想要更改其密码时,表单会要求提供当前密码和新密码-。matches()用于在将“当前”密码更改为新密码(必须再次修改)之前确认“当前”密码与现有密码匹配

使用上下文中定义的凭证处理程序对于确保密码按照tomcat要求的方式进行更改非常重要。另一种选择是让每个应用程序都提供自己的对应库,考虑到tomcat已经有了这些库,这看起来既浪费又容易出错

克里斯托弗·舒尔茨(Christopher Schultz)在我问题开头的链接中描述了这一切

我认为这是非常标准的,Tomcat为此提供了
CredentialHandler ch=(CredentialHandler)application.getAttribute(Globals.CREDENTIAL\u HANDLER)
。问题是,该实现没有考虑使用的LockoutRealm——它假设凭证处理程序直接定义在顶级领域下,所以我想知道这是tomcat中的一个疏忽/错误,还是它的工作原理与设计类似,以及是否有某种方法可以访问.mutate()和.matches()定义的
CredentialHandler
的函数,同时也使用
LockOutRealm

另外,tomcat曾经为这个用例提供
RealmBase.Digest(字符串凭证、字符串算法、字符串编码)
,但是该方法在8.5中被弃用,在9中被删除,所以我理解
CredentialHandler ch=(CredentialHandler)application.getAttribute(Globals.CREDENTIAL\u HANDLER)
是我们应该采用的新方式


编辑2:

getAttribute(Globals.CREDENTIAL\u HANDLER)
返回一个根本不加密密码的默认
CredentialHandler
。调试器显示类为
StandardContext
。在代码中单步执行,这一切似乎都是正确的,但它没有深入到
数据源ALM
以获取定义的
NestedCredentialHandler
,而是查看
LockoutRealm
,没有找到直接的子
CredentialHandler
,因此创建并返回一个默认的。我相信这一点如以下文件所述:

CredentialHandler元素必须嵌套在域组件中。如果未包含,将使用MessageDigestCredentialHandler创建默认的CredentialHandler


现在用例已经清楚了。你在编辑中提到的是正确的。它从类开始(请参阅:受保护的void startInternal()方法)。如果凭证处理程序为null(在您的案例中,它位于LockoutRealm上),则会将默认凭证处理程序设置为MessageDigestCredentialHandler,而不使用任何算法,则凭证处理程序的行为(如果未指定算法)是使用纯文本

接下来看一下(请参阅:受保护的同步void startInternal()方法,第5016-5027行),第5019和5024行很重要。在这里,通过“getRealmInternal()”调用检索域,该调用返回配置的域,在您的情况下为LockoutRealm,后续的“getCredentialHandler()”返回上述默认凭据处理程序(由于该方法被分派到RealmBase,需要注意的一点是,CombinedRealm或LockoutRealm不会覆盖该方法

这就是为什么密码没有被修改

您所需要的看起来是可能的。从上面可以明显看出,当对领域调用“getCredentialHandler()”时,领域应该处理它,而不是分派给RealmBase

因此,您可以执行以下操作:

  • 使用realm类(比如XRealm)扩展LockoutRealm
  • 重写getCredentialHandler方法
  • 在方法中,访问CombinedRealm的“受保护的最终列表领域”,您的DataSourceRealm应该是第0个元素
  • 调用DataSourceALM上的getCredentialHandler并返回
  • 当StandardContext调用getCredentialHandler时,它应该获取您配置的NestedCredentialHandler

  • 以上应该可以解决问题。

    现在用例已经清楚了。您在编辑中提到的内容是正确的。它从类开始(请参阅:protected void startInternal()方法)。如果凭据处理程序为null(在您的情况下,它位于LockoutRealm上),则为空,将默认凭据处理程序设置为MessageDigestCredentialHandler(不带任何算法),如果没有算法,则设置凭据处理程序的行为