Kerberos 委派:GSSUtil.createSubject returns 主体仅具有主体名称

Kerberos delegation: GSSUtil.createSubject returns subject with principal name only

我正在进行 kerberos 授权。我注意到 GSSUtil.createSubject(context.getSrcName(), clientCred) returns 一个没有凭据的主题。 在此之前,我已经完成 GSSCredential clientCred = context.getDelegCred(); 其中 returns 凭据。 编辑:当我从同一域中的一台机器访问我的服务时,它可以工作,而如果从同一域中的其他机器访问,它就不会。 对 AD 需要哪些额外设置感到困惑? 非常感谢任何帮助。

以下是我的代码:

public class KerberosTest {

    public Subject loginImpl(byte[] kerberosTicket, String propertiesFileName) throws Exception {
        System.setProperty("sun.security.krb5.debug", "true");
//        // no effect // System.setProperty("javax.security.auth.useSubjectCredsOnly","false");

        final Krb5LoginModule krb5LoginModule = new Krb5LoginModule();
        Subject serviceUserSubject = new Subject();
        final Map<String,String> optionMap = new HashMap<String,String>();
        HashMap<String, String> shared = new HashMap<>();

            optionMap.put("keyTab", "C:\kerberos_files\sapuser.keytab");
            optionMap.put("principal", "HTTP/SAPTEST@EQSECTEST.LOCAL"); // default realm
//            optionMap.put("principal", "kerberosuser"); // default realm
            optionMap.put("useFirstPass", "true");
            optionMap.put("doNotPrompt", "true");
            optionMap.put("refreshKrb5Config", "true");
            optionMap.put("useTicketCache", "false");
            optionMap.put("renewTGT", "false");
            optionMap.put("useKeyTab", "true");
            optionMap.put("storeKey", "true");
            optionMap.put("isInitiator", "true");
            optionMap.put("useSubjectCredsOnly", "false");

            optionMap.put("debug", "true"); // switch on debug of the Java implementation
            krb5LoginModule.initialize(serviceUserSubject, null, shared, optionMap);

            // login using details mentioned inside keytab
            boolean loginOk = krb5LoginModule.login();
            System.out.println("Login success: " + loginOk);

            // This API adds Kerberos Credentials to the the Subject's private credentials set
            boolean commitOk = krb5LoginModule.commit();

        }

        System.out.println("Principal from subject: " + serviceUserSubject.getPrincipals()); // this must display name of user to which the keytab corresponds to



        Subject clientSubject = getClientContext(serviceUserSubject, kerberosTicket);
        System.out.println("Client Subject-> " + clientSubject);
        System.out.println("Client principal-> "+clientSubject.getPrincipals().toArray()[0]);

        return clientSubject;
    }

    // Completes the security context initialisation and returns the client name.
    private Subject getClientContext(Subject subject, final byte[] kerberosTicket) throws PrivilegedActionException {
        Subject clientSubject = Subject.doAs(subject, new KerberosValidateAction(kerberosTicket));
        return clientSubject;
    }

    private class KerberosValidateAction implements PrivilegedExceptionAction<Subject> {
        byte[] kerberosTicket;

        public KerberosValidateAction(byte[] kerberosTicket) {
            this.kerberosTicket = kerberosTicket;
        }

        @Override
        public Subject run() throws Exception {
            GSSManager gssManager = GSSManager.getInstance();
            GSSContext context = gssManager.createContext((GSSCredential) null);

            Oid kerberosOid = new Oid("1.2.840.113554.1.2.2");

//             context.requestCredDeleg(true); // needed when we are demanding ticket from KDC. In our scenario, we are getting ticket from browser(client)

            // Called by the context acceptor upon receiving a token from the peer. This is our context acceptor
            // This method may return an output token which the application will need to send to the peer for further processing by its initSecContext call.
            // We will only accept the incoming token from Peer (browser) and fwd it to third party system
            while (!context.isEstablished()) {
                byte[] nextToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
            }

            boolean established = context.isEstablished();
            String user = context.getSrcName().toString();
            String serviceAccnt = context.getTargName().toString();


            //check if the credentials can be delegated
            if (!context.getCredDelegState()) {
                System.out.println("credentials can not be delegated!");
                return null;
            }

            //get the delegated credentials from the calling peer...
            GSSCredential clientCred = context.getDelegCred();
            //Create a Subject out of the delegated credentials.
            //With this Subject the application server can impersonate the client that sent the request.

            Subject clientSubject = GSSUtil.createSubject(context.getSrcName(), clientCred);
            return clientSubject; // ***this contains only principal name and not credentials !!
        }
    }

我想出了一个重要的观点,但我在任何地方都没有找到它的记录。 -

在 AD 中配置 kerberos 服务用户时,"Trust this user for delegation (Kerberos Only)" 仅对保存此更改后创建的新 TGT 生效。

例如,如果用户在客户端计算机 A 上登录,比如在 10:00 AM。
管理员在上午 10:30 启用对 kerberos 服务用户的委派。
当用户从机器 A 上的浏览​​器点击应用程序 URL 时,委托将不起作用,因为他的 TGT 是在委托打开之前创建的(在 10:00 AM)。

如果用户从机器A注销并重新登录,将生成一个新的TGT。委派现在非常适合该用户。