Springboot 系列 (30) - Springboot+Kerberos (一) | Springboot Client/Server 程序通过 SASL/GSSAPI 实现 Kerberos 认证


Kerberos (Secure Network Authentication System,网络安全认证系统),是一种网络认证协议,其设计目标是通过密钥系统为 Client/Server 提供强大的认证服务。该认证过程的实现不依赖于主机操作系统的认证,无需基于的信任,不要求网络上所有主机的物理安全,并假定网络上传送的数据包可以被任意地读取、修改和插入数据。

SASL (Simple Authentication and Security Layer,简单授权和安全层),是一个互联网标准,它制定了一个授权协议,在 Client/Server 之间建立连接。SASL 定义了授权数据如何交换,但是并没有制定数据的内容。它是一个授权机制框架。

GSSAPI (Generic Security Services Application Program Interface,通用安全服务应用程序接口),也称 GSS-API,是程序访问安全服务的应用程序编程接口。GSSAPI 是 IETF 标准,用于解决当今使用的许多类似但不兼容的安全服务的问题。

GSSAPI 本身是一个独立的认证框架外,它同时也适配了 SASL,也就是说 GSSAPI 同时也是 SASL 规范下的一种认证机制,这就使得 SASL 可以通过 GSSAPI 间接支持 Kerberos,本文我们将使用 SASL/GSSAPI 指代两者。

Kerberos 的基本介绍和安装配置,可以参考 “Linux基础知识(16)- Kerberos (一) | Kerberos 安装配置”。

本文创建两个 Springboot 程序 Client 和 Server,演示通过 SASL/GSSAPI 实现 Kerberos 认证。

1. 系统环境

    操作系统:Ubuntu 20.04
    Java 版本:openjdk 11.0.18

    本文 Kerberos 的客户端和服务端都安装在同一台主机上,主机名为 hadoop-master-vm,Springboot 程序也运行在 hadoop-master-vm 上。
 


2. 创建 Springboot 项目

    Windows版本:Windows 10 Home (20H2)   
    IntelliJ IDEA:Community Edition for Windows 2020.1.4
    Apache Maven:3.8.1

    注:Spring 开发环境的搭建,可以参考 “ Spring基础知识(1)- Spring简介、Spring体系结构和开发环境配置 ”。

    1) 运行 IDEA 创建一个空项目
   
        点击菜单 New 创建 Project:
        
        New Project -> Empty Project -> Next

            Project Name: SpringbootExample24
            Project location: 指定一个目录,比如 D:\Workshop\idea\SpringbootExample24

        -> Finish

    2) 添加 Server 模块 (Module)

        点击菜单 File -> New 创建 Module:

        New Module -> Maven -> Project Type: Maven -> Project SDK: 1.8 -> Check "Create from archtype" -> select "org.apache.maven.archtypes:maven-archtype-quickstart" -> Next

            Name: Server  
            Location: D:\Workshop\idea\SpringbootExample24\Server

            GroupId: com.example
            ArtifactId: Server
            Version: 1.0-SNAPSHOT

        -> Next

            Maven home directory: D:/Apps/Java/apache-maven-3.8.1  (本文的配置路径,下同)
            User settings file: D:\Apps\Java\apache-maven-3.8.1\conf\settings.xml
            Local repository: D:\Apps\Java\maven-repository

        -> Finish

     3) 添加 Client 模块 (Module)

        点击菜单 File -> New 创建 Module:

        New Module -> Maven -> Project Type: Maven -> Project SDK: 1.8 -> Check "Create from archtype" -> select "org.apache.maven.archtypes:maven-archtype-quickstart" -> Next

            Name: Client  
            Location: D:\Workshop\idea\SpringbootExample24\Client  

            GroupId: com.example
            ArtifactId: Client
            Version: 1.0-SNAPSHOT

        -> Next

            Maven home directory: D:/Apps/Java/apache-maven-3.8.1
            User settings file: D:\Apps\Java\apache-maven-3.8.1\conf\settings.xml
            Local repository: D:\Apps\Java\maven-repository
        
        -> Finish        


3. Server 模块 (Module)

    1) 修改 pom.xml

复制代码
        <?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
                                    http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>

            <groupId>com.example</groupId>
            <artifactId>Server</artifactId>
            <version>1.0-SNAPSHOT</version>

            <name>Server</name>
            <!-- FIXME change it to the project's website -->
            <url>http://www.example.com</url>

            <properties>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
            </properties>

            <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>2.6.6</version>
                <relativePath/> <!-- lookup parent from repository -->
            </parent>

            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-test</artifactId>
                    <scope>test</scope>
                </dependency>            
                <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>4.11</version>
                    <scope>test</scope>
                </dependency>
            </dependencies>
            
            <build>
                <finalName>Server</finalName>
                <plugins>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <mainClass>com.example.ServerApp</mainClass>
                            <layout>JAR</layout>
                        </configuration>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>repackage</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>

                <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
                    <plugins>
                        ...
                    </plugins>
                </pluginManagement>
            </build>
        </project>
复制代码


        在IDE中项目列表 -> Server -> 点击鼠标右键 -> Maven -> Reload Project

        本文选择了 spring-boot-starter-parent 2.6.6 相关依赖包,spring-boot-starter 和 spring-boot-starter-test 的版本由 spring-boot-starter-parent 控制。

    2) 配置文件
    
        添加 src/main/resources/application.properties 文件,内容如下:
    
            spring.main.banner-mode=off

        添加 src/main/resources/krb5_testsrc.keytab 文件,这里使用 “Linux基础知识(17)- Kerberos (二) | krb5 API 的 C 程序示例” 里创建的 krb5_testsrc.keytab 文件。

    3) 添加 src/main/java/com/example/ServerApp.java 文件

复制代码
        package com.example;

        import java.net.ServerSocket;
        import java.net.Socket;
        import java.util.Base64;
        import java.util.HashMap;
        import java.util.HashSet;
        import java.util.Set;
        import javax.security.auth.Subject;
        import javax.security.auth.kerberos.KerberosPrincipal;
        import javax.security.auth.login.AppConfigurationEntry;
        import javax.security.auth.login.LoginContext;
        import javax.security.auth.login.LoginException;
        import java.security.Principal;
        import java.security.PrivilegedActionException;
        import java.security.PrivilegedExceptionAction;
        import java.io.*;
        import org.ietf.jgss.*;
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;

        @SpringBootApplication
        public class ServerApp {
            private static String strRealm = "hadoop.com";
            private static String strPrincipalServer = "testsrv/hadoop-master-vm";
            private static String strKeytab = "krb5_testsrv.keytab";
            private static String strKrb5MechOid = "1.2.840.113554.1.2.2";
            private static String strSpnegoOid = "1.3.6.1.5.5.2";
            private static String strKdcServer = "hadoop-master-vm";
            private static String strServerHost = "hadoop-master-vm";
            private static int iServerPort = 9988;

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

                System.setProperty("java.security.krb5.realm", strRealm);
                System.setProperty("java.security.krb5.kdc", strKdcServer);

                javax.security.auth.login.Configuration config = new javax.security.auth.login.Configuration() {
                    @Override
                    public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
                        HashMap<String, Object> options = new HashMap<String, Object>() {
                            {
                                put("useKeyTab", "true");
                                put("keyTab", strKeytab);
                                put("principal", strPrincipalServer);
                                put("doNotPrompt", "true");
                                put("storeKey", "true");
                                put("isInitiator", "true");
                                put("debug", "true");
                            }
                        };

                        return new AppConfigurationEntry[]{
                                new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
                                        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options)
                        };
                    }
                };

                try {

                    System.out.println("Server Krb5Login ... ");
                    final Set<Principal> principalSet = new HashSet<Principal>(1);
                    principalSet.add(new KerberosPrincipal(strPrincipalServer));

                    Subject subject = new Subject(false, principalSet, new HashSet<Object>(), new HashSet<Object>());
                    LoginContext loginContext = new LoginContext(strPrincipalServer, subject, null, config);
                    loginContext.login();

                    Subject serviceSubject = loginContext.getSubject();

                    System.out.println("Subject.doAs() ... ");
                    GSSCredential gssCredential = Subject.doAs(serviceSubject, new PrivilegedExceptionAction<GSSCredential>() {
              
                        final Oid krb5MechOid = new Oid(strKrb5MechOid);
              final Oid spnegoOid = new Oid(strSpnegoOid);
@Override
public GSSCredential run() throws Exception { GSSManager manager = GSSManager.getInstance(); GSSName gssServerName = manager.createName(strPrincipalServer, GSSName.NT_USER_NAME); GSSCredential serverGssCreds = manager.createCredential(gssServerName, GSSCredential.INDEFINITE_LIFETIME, krb5MechOid, // spnegoOid or krb5MechOid, consistent with the GSSContext settings on the client GSSCredential.ACCEPT_ONLY); return serverGssCreds; } }); if (gssCredential == null) { System.out.println("gssCredential == null"); return; } // ServerSocket serverSocket = new ServerSocket(iServerPort); OUTER: while (true) { System.out.println("serverSocket.accept() ..."); Socket connSocket = serverSocket.accept(); DataInputStream inStream = new DataInputStream(connSocket.getInputStream()); DataOutputStream outStream = new DataOutputStream(connSocket.getOutputStream()); System.out.println("client:" + connSocket.getInetAddress()); GSSManager manager = GSSManager.getInstance(); GSSName gssServerName = manager.createName(strPrincipalServer, GSSName.NT_USER_NAME); GSSContext gssContext = manager.createContext(gssServerName, null, gssCredential, GSSContext.DEFAULT_LIFETIME); // Do the context establish loop byte[] token = null; while (!gssContext.isEstablished()) { token = new byte[inStream.readInt()]; inStream.readFully(token); byte[] decodedToken = Base64.getDecoder().decode(token); System.out.println("gssContext.acceptSecContext(): decodedToken.length == " + decodedToken.length); token = gssContext.acceptSecContext(decodedToken, 0, decodedToken.length); // Send a token to the peer if one was generated by // acceptSecContext if (token != null) { System.out.println("outStream.writeInt(): token.length == " + token.length); outStream.writeInt(token.length); outStream.write(token); outStream.flush(); } } System.out.println("gssContext.isEstablished() == " + gssContext.isEstablished()); System.out.println("client: " + gssContext.getSrcName()); System.out.println("server: " + gssContext.getTargName()); if (gssContext.getMutualAuthState()) System.out.println("Mutual authentication is enable!"); // Normal message loop int done = 0; int count = 0; byte[] data = new byte[256]; do { try { count = inStream.readInt(); inStream.read(data); } catch (EOFException e) { System.out.println("EOFException(): client exit or network broken"); break; } if (count <= 0) { if (count < 0) { System.out.println("in.read(): error -> count == " + count); break; } done = 1; System.out.println("in.read(): done == " + done); } // Shutdown from client String str = new String(data); if ("shutdown".equals(str.substring(0, 8))) { System.out.println(str); connSocket.close(); gssContext.dispose(); break OUTER; } System.out.println("in.read(): from client -> " + str); Thread.sleep(2000); if (done <= 0) { outStream.writeInt(str.length()); outStream.write(data); outStream.flush(); System.out.println("outStream.write(): to client -> " + str); } } while (done <= 0); /* // Security message channel MessageProp prop = new MessageProp(0, false); token = new byte[inStream.readInt()]; System.out.println("Will read token of size " + token.length); inStream.readFully(token); byte[] bytes = gssContext.unwrap(token, 0, token.length, prop); String str = new String(bytes); System.out.println("Received data \"" + str + "\" of length " + str.length()); System.out.println("Confidentiality applied: " + prop.getPrivacy()); prop.setQOP(0); token = gssContext.getMIC(bytes, 0, bytes.length, prop); System.out.println("Will send MIC token of size " + token.length); outStream.writeInt(token.length); outStream.write(token); outStream.flush(); */ System.out.println("connSocket.close()"); connSocket.close(); gssContext.dispose(); } serverSocket.close(); } catch (LoginException e) { e.printStackTrace(); } catch (PrivilegedActionException e) { e.printStackTrace(); } catch (GSSException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }
复制代码

 


4. Client 模块 (Module)

    1) 修改 pom.xml

复制代码
        <?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
                                    http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>

            <groupId>com.example</groupId>
            <artifactId>Client</artifactId>
            <version>1.0-SNAPSHOT</version>

            <name>Client</name>
            <!-- FIXME change it to the project's website -->
            <url>http://www.example.com</url>

            <properties>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
            </properties>

            <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>2.6.6</version>
                <relativePath/> <!-- lookup parent from repository -->
            </parent>

            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-test</artifactId>
                    <scope>test</scope>
                </dependency>            
                <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>4.11</version>
                    <scope>test</scope>
                </dependency>
            </dependencies>
            
            <build>
                <finalName>Client</finalName>
                <plugins>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <mainClass>com.example.ClientApp</mainClass>
                            <layout>JAR</layout>
                        </configuration>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>repackage</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>

                <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
                    <plugins>
                        ...
                    </plugins>
                </pluginManagement>
            </build>
        </project>
复制代码


        在IDE中项目列表 -> Server -> 点击鼠标右键 -> Maven -> Reload Project

        本文选择了 spring-boot-starter-parent 2.6.6 相关依赖包,spring-boot-starter 和 spring-boot-starter-test 的版本由 spring-boot-starter-parent 控制。

    2) 配置文件
    
        添加 src/main/resources/application.properties 文件,内容如下:
    
            spring.main.banner-mode=off

        添加 src/main/resources/krb5_testcli.keytab 文件,这里使用 “Linux基础知识(17)- Kerberos (二) | krb5 API 的 C 程序示例” 里创建的 krb5_testcli.keytab 文件。

    3) 添加 src/main/java/com/example/ClientApp.java 文件

复制代码
        package com.example;

        import java.net.Socket;
        import java.util.Scanner;
        import java.util.HashMap;
        import java.util.Set;
        import java.util.HashSet;
        import java.util.Base64;
        import javax.security.auth.Subject;
        import javax.security.auth.kerberos.KerberosPrincipal;
        import javax.security.auth.login.AppConfigurationEntry;
        import javax.security.auth.login.LoginContext;
        import javax.security.auth.login.LoginException;
        import java.security.Principal;
        import java.security.PrivilegedActionException;
        import java.security.PrivilegedExceptionAction;
        import java.io.*;
        import org.ietf.jgss.*;
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;

        @SpringBootApplication
        public class ClientApp {
            private static String strRealm = "hadoop.com";
            private static String strPrincipalClient = "testcli";
            private static String strPrincipalServer = "testsrv/hadoop-master-vm";
            private static String strKeytab = "krb5_testcli.keytab";
            private static String strSpnegoOid = "1.3.6.1.5.5.2";
            private static String strKrb5MechOid = "1.2.840.113554.1.2.2";
            private static String strKdcServer = "hadoop-master-vm";
            private static String strServerHost = "hadoop-master-vm";
            private static int iServerPort = 9988;

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

                // Config
                System.setProperty("java.security.krb5.realm", strRealm);
                System.setProperty("java.security.krb5.kdc", strKdcServer);

                javax.security.auth.login.Configuration config = new javax.security.auth.login.Configuration() {
                    @Override
                    public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
                        HashMap<String, Object> options = new HashMap<String, Object>() {
                            {
                                put("useKeyTab", "true");
                                put("keyTab", strKeytab);
                                put("principal", strPrincipalClient);
                                put("doNotPrompt", "true");
                                put("storeKey", "true");
                                put("isInitiator", "true");
                                put("debug", "true");
                            }
                        };

                        return new AppConfigurationEntry[]{
                                new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
                                        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options)
                        };
                    }
                };

                try {

                    System.out.println("Client Krb5Login ... ");
                    final Set<Principal> principalSet = new HashSet<Principal>(1);
                    principalSet.add(new KerberosPrincipal(strPrincipalClient));

                    Subject subject = new Subject(false, principalSet, new HashSet<Object>(), new HashSet<Object>());
                    LoginContext loginContext = new LoginContext(strPrincipalClient, subject, null, config);
                    loginContext.login();
                    Subject serviceSubject = loginContext.getSubject();

                    System.out.println("Subject.doAs() ... ");
                    GSSContext gssContext = Subject.doAs(serviceSubject, new PrivilegedExceptionAction<GSSContext>() {

                        final Oid krb5MechOid = new Oid(strKrb5MechOid);  // Kerberos authentication
final Oid spnegoOid = new Oid(strSpnegoOid); // SPNEGO authentication @Override
public GSSContext run() throws Exception { GSSManager manager = GSSManager.getInstance(); // GSSName gssClientName = manager.createName(strPrincipalClient, GSSName.NT_USER_NAME); GSSCredential clientGssCreds = manager.createCredential(gssClientName, GSSCredential.INDEFINITE_LIFETIME, krb5MechOid, GSSCredential.INITIATE_ONLY); // GSS ticket or token GSSName gssServerName = manager.createName(strPrincipalServer, GSSName.NT_USER_NAME); GSSContext context = manager.createContext(gssServerName, null, // spnegoOid, krb5MechOid or null (null equals Kerberos authentication) clientGssCreds, GSSContext.DEFAULT_LIFETIME); return context; } }); if (gssContext == null) { System.out.println("gssContext == null"); return; } gssContext.requestCredDeleg(true); gssContext.requestMutualAuth(true); // Mutual authentication gssContext.requestConf(true); // Will use confidentiality later gssContext.requestInteg(true); // Will use integrity later // Connect to server Socket clientSocket = new Socket(strServerHost, iServerPort); DataInputStream inStream = new DataInputStream(clientSocket.getInputStream()); DataOutputStream outStream = new DataOutputStream(clientSocket.getOutputStream()); System.out.println("Connected to server: " + clientSocket.getInetAddress()); // Do the context loop byte[] token = new byte[0]; while (!gssContext.isEstablished()) { token = gssContext.initSecContext(token, 0, token.length); // Send a token to the server if one was generated by // initSecContext if (token != null) { byte[] encodedToken = Base64.getEncoder().encode(token); System.out.println("outStream.writeInt(): encodedToken.length == " + encodedToken.length); outStream.writeInt(encodedToken.length); outStream.write(encodedToken); outStream.flush(); } // If the client is done with context establishment // then there will be no more tokens to read in this loop if (!gssContext.isEstablished()) { token = new byte[inStream.readInt()]; System.out.println("inStream.writeInt(): token.length == " + token.length); inStream.readFully(token); } } System.out.println("gssContext.isEstablished() == " + gssContext.isEstablished()); System.out.println("client: " + gssContext.getSrcName()); System.out.println("server: " + gssContext.getTargName()); if (gssContext.getMutualAuthState()) System.out.println("Mutual authentication is enable!"); /* // Security message channel byte[] messageBytes = "Hello There!\0".getBytes(); MessageProp prop = new MessageProp(0, true); token = gssContext.wrap(messageBytes, 0, messageBytes.length, prop); System.out.println("Will send wrap token of size " + token.length); outStream.writeInt(token.length); outStream.write(token); outStream.flush(); token = new byte[inStream.readInt()]; System.out.println("Will read token of size " + token.length); inStream.readFully(token); gssContext.verifyMIC(token, 0, token.length, messageBytes, 0, messageBytes.length, prop); System.out.println("Verified received MIC for message."); */ // Normal message loop Scanner sc = new Scanner(System.in); System.out.print("Input> "); String str = sc.next(); byte[] data = new byte[256]; int count = 0; while (!str.equals("exit") && !str.equals("quit")) { outStream.writeInt(str.length()); outStream.write(str.getBytes()); if (str.equals("shutdown")) break; // InputStream in = clientSocket.getInputStream(); count = inStream.readInt(); inStream.read(data); if (count > 0) { System.out.println("in.read(): from server -> " + new String(data)); } else { System.out.println("in.read(): count == " + count); break; } System.out.print("Input> "); str = sc.next(); } System.out.println("Exiting ..."); sc.close(); clientSocket.close(); gssContext.dispose(); } catch (LoginException e) { e.printStackTrace(); } catch (PrivilegedActionException e) { e.printStackTrace(); } catch (GSSException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }
复制代码

 


5. 运行

    1) 打包 Jar

        菜单 View -> Tool Windows -> Maven -> Client -> Lifecycle -> Clean & Package

            Client 模块 (Module) 的 Jar 包生成在 Client 模块下的目录 target/ 里

                Client.jar
                Client.jar.original  

            注:Client.jar  包含依赖包,可以直接运行。 Client.jar.original 里不包含依赖的包(要手动配置依赖环境),运行前要把文件名上的 “.original” 去掉。

        菜单 View -> Tool Windows -> Maven -> Server -> Lifecycle -> Clean & Package

            Server 模块 (Module) 的 Jar 包生成在 Server 模块下的目录 target/ 里

                Server.jar
                Server.jar.original

    2) 运行 Jar

        把 Server.jar 和 Client.jar 复制到主机 hadoop-master-vm 上,在两个控制台分别运行这两个 Jar 包,运行命令如下。

        控制台1:
        
            $ java -jar Server.jar

复制代码
                ...

                Server Krb5Login ...
                Debug is  true storeKey true useTicketCache false useKeyTab true doNotPrompt true ticketCache is null isInitiator true KeyTab is krb5_testsrv.keytab refreshKrb5Config is false principal is testsrv/hadoop-master-vm tryFirstPass is false useFirstPass is false storePass is false clearPass is false
                principal is testsrv/hadoop-master-vm@hadoop.com
                Will use keytab
                Commit Succeeded

                Subject.doAs() ...
                serverSocket.accept() ...
                client: /192.168.1.5
                gssContext.acceptSecContext(): token.length == 1600
                outStream.writeInt(): token.length == 108
                gssContext.isEstablished() == true
                client: testcli@hadoop.com
                server: testsrv/hadoop-master-vm
                Mutual authentication is enable!
复制代码


        控制台2:

            $ java -jar Client.jar

复制代码
                Client Krb5Login ...
                Debug is  true storeKey true useTicketCache false useKeyTab true doNotPrompt true ticketCache is null isInitiator true KeyTab is krb5_testcli.keytab refreshKrb5Config is false principal is testcli tryFirstPass is false useFirstPass is false storePass is false clearPass is false
                principal is testcli@hadoop.com
                Will use keytab
                Commit Succeeded

                Subject.doAs() ...
                Connected to server: hadoop-master-vm/192.168.1.5
                outStream.writeInt(): encodedToken.length == 1600
                inStream.writeInt(): token.length == 108
                gssContext.isEstablished() == true
                client: testcli
                server: testsrv/hadoop-master-vm
                Mutual authentication is enable!
                Input> test
复制代码


            注: 输入文本 “test”,按回车键,Server 收到 “test” 后会发回 Client。输入 “exit” 或 “quit”,可以退出 Client 程序,Server 程序继续处于 accept 状态。输入 “shutdown”,Server 和 Client 都退出。


参考来源:https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/BasicClientServer.html

posted @   垄山小站  阅读(1870)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
点击右上角即可分享
微信分享提示