前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >java连接kerberos用户认证

java连接kerberos用户认证

原创
作者头像
刘大猫
发布2025-01-16 21:33:47
发布2025-01-16 21:33:47
1000
举报
文章被收录于专栏:LDAP+KerberosLDAP+Kerberos

@toc

一、背景

亲测可用,之前搜索了很多博客,啥样的都有,就是不介绍报错以及配置用处,根本不懂照抄那些配置是干啥的,稀里糊涂的按照博客搭完也跑不起来,因此记录这个。

项目背景:公司项目当前采用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:如果里面的某些配置不知道在哪或者不知道干啥的,可以看我的前面的博客,详细介绍了安装配置等,可以大致了解参数。

目前网上相关文章很少,而且好多博客都是未认证就发布的所以一堆问题,跑不起来,如下是我参考的博客

二、代码

2.1目录

在这里插入图片描述
在这里插入图片描述

2.2配置文件application.properties

代码语言:java
复制
server.port=8020

2.3pom依赖

代码语言:java
复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>1.4.7.RELEASE</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>ldap-test2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ldap-test2</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--解决@RestController注解爆红-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <!--测试类-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-security-core</artifactId>
                    <groupId>org.springframework.security</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-ldap</artifactId>
            <version>2.6.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-ldap</artifactId>
            <version>2.6.3</version>
        </dependency>
        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>ldapbp</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.api</groupId>
            <artifactId>api-all</artifactId>
            <version>2.0.0</version>
        </dependency>

        <!-- Spring Security Kerberos -->
        <dependency>
            <groupId>org.springframework.security.kerberos</groupId>
            <artifactId>spring-security-kerberos-core</artifactId>
            <version>1.0.1.RELEASE</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring-security-core</artifactId>
                    <groupId>org.springframework.security</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.kerberos</groupId>
            <artifactId>spring-security-kerberos-client</artifactId>
            <version>1.0.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.kerberos</groupId>
            <artifactId>spring-security-kerberos-web</artifactId>
            <version>1.0.1.RELEASE</version>
        </dependency>

        <!-- Additional dependencies for Spring LDAP and Security -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-ldap</artifactId>
            <version>4.2.3.RELEASE</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring-security-core</artifactId>
                    <groupId>org.springframework.security</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.example.ldaptest2.LdapTest2Application</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.4代码

AuthProviderConfig配置类

说明:注释的代码还没放开,目前只是demo阶段,后续需要集成到自己项目 ,肯定需要配置哪些路径访问权限放开,如果没权限自动跳转项目登录页等等,后续需要再配置的。

代码语言:java
复制
package com.example.ldaptest2.config;

import com.example.ldaptest2.service.DummyUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;

@Configuration
@EnableWebMvcSecurity
public class AuthProviderConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/ldap/*").permitAll()
            .anyRequest().authenticated();
//            .and()
//            .formLogin()
//            .loginPage("/login").permitAll()
//            .and()
//            .logout()
//            .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(kerberosAuthenticationProvider());
    }

    @Bean
    public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
        KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
        SunJaasKerberosClient client = new SunJaasKerberosClient();
        client.setDebug(true);
        provider.setKerberosClient(client);
        provider.setUserDetailsService(dummyUserDetailsService());
        return provider;
    }

    @Bean
    public DummyUserDetailsService dummyUserDetailsService() {
        return new DummyUserDetailsService();
    }

}

CustomConfigurationByKeytab配置类

代码语言:java
复制
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", "C:\\Users\\211145187\\Desktop\\fsdownload\\ldap.keytab"); //指定 keytab 文件的路径,这里设置为 "/etc/openldap/ldap.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;
    }
}

CustomConfigurationByPassword配置类

代码语言:java
复制
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 CustomConfigurationByPassword extends Configuration {
    @Override
    public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
        if ("KrbLogin".equals(name)) {
            Map<String, String> options = new HashMap<>();
            options.put("useKeyTab", "false");   //指定是否使用 keytab 文件进行登录,这里设置为 true,表示使用 keytab 文件。
            options.put("storeKey", "true");    //指定是否将密钥存储在 Subject 中,这里设置为 true,表示存储密钥。
            options.put("useTicketCache", "false"); //指定是否使用票据缓存,这里设置为 false,表示不使用票据缓存。
            options.put("doNotPrompt", "false"); //指定是否禁止提示用户输入用户名和密码,这里设置为 true,表示禁止提示。
            options.put("debug", "true");   //指定是否启用调试模式,这里设置为 true,表示启用调试模式。
            options.put("password", "123456"); //指定要使用的主体名称,这里设置为 "ldap/bridge1@NODE3.COM",表示使用的服务主体。
            options.put("principal", "testldap3@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;
    }
}

TestController

注意:其实这个controller可有可无,因为你写java客户端连接kerberos,如果不涉及打包部署linux环境通过url方式掉方法,完全可以不写这个,只写本地测试方法即可。

代码语言:java
复制
package com.example.ldaptest2.controller;

import com.example.ldaptest2.config.CustomConfigurationByKeytab;
import com.example.ldaptest2.config.CustomConfigurationByPassword;
import com.example.ldaptest2.entity.LdapUser;
import com.example.ldaptest2.entity.MyCallbackHandler;
import com.example.ldaptest2.mapper.LdapUserAttributeMapper;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;

/**
 * @Author 211145187
 * @Date 2024/6/14 17:18
 **/
@RestController
@RequestMapping("/ldap")
public class TestController {
    private static Logger logger = LoggerFactory.getLogger(TestController.class);

    //keytab认证
    @GetMapping(value = "/authenticateUserByKeytab")
    public void authenticateUserByKeytab() {
        try {
            System.setProperty("java.security.krb5.conf", "/etc/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();
            logger.error("LoginException e:{}", e.getMessage());
        }
    }

    //用户+密码认证
    @GetMapping(value = "/authenticateUserByPassword")
    public void authenticateUserByPassword() {
        try {
            System.setProperty("java.security.krb5.conf", "/etc/krb5.conf");
            // 创建 LoginContext 对象,并为其提供自定义 Configuration
            LoginContext lc = new LoginContext("KrbLogin", null, new MyCallbackHandler(), new CustomConfigurationByPassword());

            // 进行 Kerberos 认证
            lc.login();
            // 获取 Subject
            Subject subject = lc.getSubject();
//            logger.info("subject:{}", subject);
            // 在这里可以使用 subject 来执行进一步的操作,如访问受限资源
            // 登出
            lc.logout();
        } catch (LoginException e) {
            // 处理登录异常
            e.printStackTrace();
            logger.error("LoginException e:{}", e.getMessage());
        }
    }

    @GetMapping(value = "/test")
    public String test() {
        return "Hello";
    }    
}

MyCallbackHandler

代码语言:java
复制
package com.example.ldaptest2.entity;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

/**
 * @Author 211145187
 * @Date 2024/6/13 19:18
 **/
public class MyCallbackHandler implements CallbackHandler {
    @Override
    public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
        for (Callback callback : callbacks) {
            if (callback instanceof NameCallback) {
                // 处理用户名回调
                NameCallback nc = (NameCallback) callback;
                nc.setName("ldapadmin@NODE3.COM"); // 设置用户名
            } else if (callback instanceof PasswordCallback) {
                // 处理密码回调
                PasswordCallback pc = (PasswordCallback) callback;
                pc.setPassword("123456".toCharArray()); // 设置密码
            } else {
                throw new UnsupportedCallbackException(callback, "Unrecognized Callback");
                // 其他类型的回调
                // 可以根据需要处理其他类型的回调
            }
        }
    }
}

DummyUserDetailsService实现类

代码语言:java
复制
package com.example.ldaptest2.service;

import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * @Author 211145187
 * @Date 2024/6/13 15:37
 **/
public class DummyUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new User(username, "notUsed", true, true, true, true,
                AuthorityUtils.createAuthorityList("ROLE_USER"));
    }

}

LdapTest2Application启动类

代码语言:java
复制
package com.example.ldaptest2;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LdapTest2Application {

    public static void main(String[] args) {
        SpringApplication.run(LdapTest2Application.class, args);
    }
}

KerberosTest测试类

代码语言:java
复制
package com.example.ldaptest2;

import com.example.ldaptest2.config.CustomConfigurationByKeytab;
import com.example.ldaptest2.config.CustomConfigurationByPassword;
import com.example.ldaptest2.entity.MyCallbackHandler;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

@SpringBootTest
@RunWith(SpringRunner.class)
public class KerberosTest {
    private static Logger logger = LoggerFactory.getLogger(KerberosTest.class);

    //用户+密码认证
    @Test
    public void authenticateUserByPassword() {
        try {
            System.setProperty("java.security.krb5.conf", "C:\\Users\\211145187\\Desktop\\fsdownload\\krb5.conf");
            // 创建 LoginContext 对象,并为其提供自定义 Configuration
            LoginContext lc = new LoginContext("KrbLogin", null, new MyCallbackHandler(), new CustomConfigurationByPassword());

            // 进行 Kerberos 认证
            lc.login();
            // 获取 Subject
            Subject subject = lc.getSubject();
//            logger.info("subject:{}", subject);
            // 在这里可以使用 subject 来执行进一步的操作,如访问受限资源
            // 登出
            lc.logout();
        } catch (LoginException e) {
            // 处理登录异常
            e.printStackTrace();
        }
    }

    //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();
        }
    }
}

SpringVersionUtils测试类

代码语言:java
复制
package com.example.ldaptest2;

import org.junit.Test;
import org.springframework.boot.SpringBootVersion;
import org.springframework.core.SpringVersion;
import org.springframework.security.core.SpringSecurityCoreVersion;

/**
 * 获取Spring、SpringBoot版本号
 * @Author 211145187
 * @Date 2022/11/12 10:42
 **/
public class SpringVersionUtils {

    /**
     * Spring Security:4.2.3.RELEASE
     * Spring Version:4.3.9.RELEASE
     * SpringBoot Version:1.4.7.RELEASE
     */
    @Test
    public void getSpringVersion() {
        System.out.println("Spring Security:" + SpringSecurityCoreVersion.getVersion());
        String versionSpring = SpringVersion.getVersion();
        String versionSpringBoot = SpringBootVersion.getVersion();
        System.out.println("Spring Version:" + versionSpring);
        System.out.println("SpringBoot Version:" + versionSpringBoot);
    }

}

六、其他报错可参考文档

Kerberos相关问题进行故障排除| 常见错误和解决方法

本人其他相关文章链接

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

18.windows server2016搭建AD域服务器

19.java连接AD(Microsoft Active Directory)模拟用户登录认证

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景
  • 二、代码
    • 2.1目录
    • 2.2配置文件application.properties
    • 2.3pom依赖
    • 2.4代码
      • AuthProviderConfig配置类
      • CustomConfigurationByKeytab配置类
      • CustomConfigurationByPassword配置类
      • TestController
      • MyCallbackHandler
      • DummyUserDetailsService实现类
      • LdapTest2Application启动类
      • KerberosTest测试类
      • SpringVersionUtils测试类
  • 六、其他报错可参考文档
  • 本人其他相关文章链接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档