一、背景
亲测可用,之前搜索了很多博客,啥样的都有,就是不介绍报错以及配置用处,根本不懂照抄那些配置是干啥的,稀里糊涂的按照博客搭完也跑不起来,因此记录这个。
项目背景
:公司项目当前采用http协议+shiro+mysql的登录认证方式,而现在想支持ldap协议认证登录然后能够访问自己公司的项目网站。
举例说明
:假设我们公司有自己的门户网站,现在我们收购了一家公司,他们数据库采用ldap存储用户数据,那么为了他们账户能登陆我们公司项目所以需要集成,而不是再把他们的账户重新在mysql再创建一遍,万一人家有1W个账户呢,不累死了且也不现实啊。
需要安装openldap+kerberos,且ldap和kerberos安装在同一台服务器上,当前版本如下:
centos 7.9
openldap 2.4.44
phpldapadmin 1.2.5
服务器IP:10.110.38.162
Kerberos :Kerberos 5 release 1.15.1
另外介绍下我的Spring各个版本:
Spring Security:4.2.3.RELEASE
Spring Version:4.3.9.RELEASE
SpringBoot Version:1.4.7.RELEASE
注意点1:
我之所以选这么旧的版本,是因为我最后要在自己项目集成,我们项目就是上面版本附近的,所以不能选太高版本,这点请注意各版本之间的兼容性问题。
详情可看这篇博客介绍兼容版本:https://zhuanlan.zhihu.com/p/652895555
注意点2
:如果里面的某些配置不知道在哪或者不知道干啥的,可以看我的前面的博客,详细介绍了安装配置等,可以大致了解参数。
目前网上相关文章很少,而且好多博客都是未认证就发布的所以一堆问题,跑不起来,如下是我参考的博客
- Spring Security Kerberos - Reference Documentation
- MIT Kerberos Documentation
- https://www.openldap.org/
- Authentication Protocols: LDAP vs Kerberos vs OAuth2 vs SAML vs RADIUS
二、报错
完整错误
Debug is true storeKey true useTicketCache true useKeyTab true doNotPrompt true ticketCache is null isInitiator true KeyTab is /etc/openldap/ldap.keytab refreshKrb5Config is true principal is ldapadmin@NODE3.COM tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Refreshing Kerberos configuration
Acquire TGT from Cache
Principal is ldapadmin@NODE3.COM
null credentials from Ticket Cache
Key for the principal ldapadmin@NODE3.COM not available in /etc/openldap/ldap.keytab
[Krb5LoginModule] authentication failed
Unable to obtain password from user
javax.security.auth.login.LoginException: Unable to obtain password from user
at com.sun.security.auth.module.Krb5LoginModule.promptForPass(Krb5LoginModule.java:897)
at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:760)
at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:617)
at com.example.ldaptest2.KerberosTest.loginImpl(KerberosTest.java:100)
at com.example.ldaptest2.KerberosTest.authenticateUser3(KerberosTest.java:76)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
错误原因1
:加载keytab认证报错,有没有可能是keytab指向不对,比如我window环境java客户端连接远程38.162服务器kerberos进行认证,那我肯定读不到服务器文件呀,我需要把文件下载到本地进行加载
错误代码:测试类
//keytab认证
@Test
public void authenticateUserByKeytab() {
try {
System.setProperty("java.security.krb5.conf", "C:\\Users\\211145187\\Desktop\\fsdownload\\krb5.conf");
// 创建 LoginContext 对象,并为其提供自定义 Configuration
LoginContext lc = new LoginContext("KrbLogin", null, null, new CustomConfigurationByKeytab());
// 进行 Kerberos 认证
lc.login();
// 获取 Subject
Subject subject = lc.getSubject();
// logger.info("subject:{}", subject);
// 在这里可以使用 subject 来执行进一步的操作,如访问受限资源
// 登出
lc.logout();
} catch (LoginException e) {
// 处理登录异常
e.printStackTrace();
}
}
配置类
package com.example.ldaptest2.config;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @Author 211145187
* @Date 2024/6/13 16:34
**/
// 自定义 Configuration 类,用于提供 Kerberos 登录配置
public class CustomConfigurationByKeytab extends Configuration {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
if ("KrbLogin".equals(name)) {
Map<String, String> options = new HashMap<>();
options.put("useKeyTab", "true"); //指定是否使用 keytab 文件进行登录,这里设置为 true,表示使用 keytab 文件。
options.put("keyTab", "/etc/openldap/ldap.keytab"); //指定 keytab 文件的路径,这里设置为 "/etc/openldap/ldap.keytab"。
options.put("storeKey", "true"); //指定是否将密钥存储在 Subject 中,这里设置为 true,表示存储密钥。
options.put("useTicketCache", "false"); //指定是否使用票据缓存,这里设置为 false,表示不使用票据缓存。
options.put("doNotPrompt", "true"); //指定是否禁止提示用户输入用户名和密码,这里设置为 true,表示禁止提示。
options.put("debug", "true"); //指定是否启用调试模式,这里设置为 true,表示启用调试模式。
options.put("principal", "ldapadmin@NODE3.COM"); //指定要使用的主体名称,这里设置为 "ldap/bridge1@NODE3.COM",表示使用的服务主体。
// 定义 Kerberos 登录模块的配置项
AppConfigurationEntry entry = new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options
);
return new AppConfigurationEntry[]{
entry};
}
return null;
}
}
解决方案:正确代码
:
options.put("keyTab", "C:\Users\211145187\Desktop\fsdownload\ldap.keytab");
错误原因2
:如果你的项目部署在linux服务器上运行,进行接口联调报错,也有可能是你的keytab文件权限不够
解决方案
:执行下面命令赋权
# chmod 777 ***.keytab
本人其他相关文章链接
1.Centos7.9安装openldap
2.Centos7.9安装kerberos
3.Openldap集成Kerberos
4.Centos7.9安装phpldapadmin
5.java连接ldap实现用户查询功能
6.java连接kerberos用户认证
7.javax.security.auth.login.LoginException: Unable to obtain password from user
8.javax.security.auth.login.LoginException: null (68)
9.javax.security.auth.login.LoginException: Message stream modified (41)
10.javax.security.auth.login.LoginException: Checksum failed
11.javax.security.auth.login.LoginException: No CallbackHandler available to garner authentication info
12.javax.security.auth.login.LoginException: Cannot locate KDC
13.javax.security.auth.login.LoginException: Receive timed out
14.java: 无法访问org.springframework.context.ConfigurableApplicationContext
15.LDAP: error code 34 - invalid DN
16.LDAP: error code 32 - No Such Object
17.java: 无法访问org.springframework.ldap.core.LdapTemplate