6765491: Krb5LoginModule a little too restrictive, and the doc is not clear
Reviewed-by: valeriep
This commit is contained in:
parent
9f36c3b7c7
commit
5c6f569aa5
@ -86,6 +86,8 @@ import sun.misc.HexDumpEncoder;
|
||||
* the principal name from the configuration is used. In the
|
||||
* case where the principal property is not set and the principal
|
||||
* entry also does not exist, the user is prompted for the name.
|
||||
* When this property of entry is set, and <code>useTicketCache</code>
|
||||
* is set to true, only TGT belonging to this principal is used.
|
||||
*
|
||||
* <p> The following is a list of configuration options supported
|
||||
* for <code>Krb5LoginModule</code>:
|
||||
@ -101,18 +103,19 @@ import sun.misc.HexDumpEncoder;
|
||||
* to false if you do not want this module to use the ticket cache.
|
||||
* (Default is False).
|
||||
* This module will
|
||||
* search for the tickect
|
||||
* search for the ticket
|
||||
* cache in the following locations:
|
||||
* For Windows 2000, it will use Local Security Authority (LSA) API
|
||||
* to get the TGT. On Solaris and Linux
|
||||
* On Solaris and Linux
|
||||
* it will look for the ticket cache in /tmp/krb5cc_<code>uid</code>
|
||||
* where the uid is numeric user
|
||||
* identifier. If the ticket cache is
|
||||
* not available in either of the above locations, or if we are on a
|
||||
* different Windows platform, it will look for the cache as
|
||||
* not available in the above location, or if we are on a
|
||||
* Windows platform, it will look for the cache as
|
||||
* {user.home}{file.separator}krb5cc_{user.name}.
|
||||
* You can override the ticket cache location by using
|
||||
* <code>ticketCache</code>
|
||||
* <code>ticketCache</code>.
|
||||
* For Windows, if a ticket cannot be retrieved from the file ticket cache,
|
||||
* it will use Local Security Authority (LSA) API to get the TGT.
|
||||
* <P>
|
||||
* <dt><b><code>ticketCache</code></b>:</dt>
|
||||
* <dd>Set this to the name of the ticket
|
||||
@ -129,20 +132,20 @@ import sun.misc.HexDumpEncoder;
|
||||
* <dt><b><code>doNotPrompt</code></b>:</dt>
|
||||
* <dd>Set this to true if you do not want to be
|
||||
* prompted for the password
|
||||
* if credentials can
|
||||
* not be obtained from the cache or keytab.(Default is false)
|
||||
* If set to true authentication will fail if credentials can
|
||||
* not be obtained from the cache or keytab.</dd>
|
||||
* if credentials can not be obtained from the cache, the keytab,
|
||||
* or through shared state.(Default is false)
|
||||
* If set to true, credential must be obtained through cache, keytab,
|
||||
* or shared state. Otherwise, authentication will fail.</dd>
|
||||
* <P>
|
||||
* <dt><b><code>useKeyTab</code></b>:</dt>
|
||||
* <dd>Set this to true if you
|
||||
* want the module to get the principal's key from the
|
||||
* the keytab.(default value is False)
|
||||
* If <code>keyatb</code>
|
||||
* If <code>keytab</code>
|
||||
* is not set then
|
||||
* the module will locate the keytab from the
|
||||
* Kerberos configuration file.</dd>
|
||||
* If it is not specifed in the Kerberos configuration file
|
||||
* Kerberos configuration file.
|
||||
* If it is not specified in the Kerberos configuration file
|
||||
* then it will look for the file
|
||||
* <code>{user.home}{file.separator}</code>krb5.keytab.</dd>
|
||||
* <P>
|
||||
@ -210,20 +213,34 @@ import sun.misc.HexDumpEncoder;
|
||||
* exist for the username and password in the shared
|
||||
* state, or if authentication fails.
|
||||
*
|
||||
* clearPass if, true, this <code>LoginModule</code> clears the
|
||||
* username and password stored in the module's shared
|
||||
* state after both phases of authentication
|
||||
* (login and commit) have completed.
|
||||
* clearPass if, true, this LoginModule clears the
|
||||
* username and password stored in the module's shared
|
||||
* state after both phases of authentication
|
||||
* (login and commit) have completed.
|
||||
* </pre>
|
||||
* <p>If the principal system property or key is already provided, the value of
|
||||
* "javax.security.auth.login.name" in the shared state is ignored.
|
||||
* <p>When multiple mechanisms to retrieve a ticket or key is provided, the
|
||||
* preference order looks like this:
|
||||
* <ol>
|
||||
* <li>ticket cache
|
||||
* <li>keytab
|
||||
* <li>shared state
|
||||
* <li>user prompt
|
||||
* </ol>
|
||||
* <p>Note that if any step fails, it will fallback to the next step.
|
||||
* There's only one exception, it the shared state step fails and
|
||||
* <code>useFirstPass</code>=true, no user prompt is made.
|
||||
* <p>Examples of some configuration values for Krb5LoginModule in
|
||||
* JAAS config file and the results are:
|
||||
* <ul>
|
||||
* <p> <code>doNotPrompt</code>=true;
|
||||
* </ul>
|
||||
* <p> This is an illegal combination since <code>useTicketCache</code>
|
||||
* is not set and the user can not be prompted for the password.
|
||||
* <p> This is an illegal combination since none of <code>useTicketCache</code>,
|
||||
* <code>useKeyTab</code>, <code>useFirstPass</code> and <code>tryFirstPass</code>
|
||||
* is set and the user can not be prompted for the password.
|
||||
*<ul>
|
||||
* <p> <code>ticketCache</code> = < filename >;
|
||||
* <p> <code>ticketCache</code> = <filename>;
|
||||
*</ul>
|
||||
* <p> This is an illegal combination since <code>useTicketCache</code>
|
||||
* is not set to true and the ticketCache is set. A configuration error
|
||||
@ -240,9 +257,9 @@ import sun.misc.HexDumpEncoder;
|
||||
*</ul>
|
||||
* <p> This is an illegal combination since <code>storeKey</code> is set to
|
||||
* true but the key can not be obtained either by prompting the user or from
|
||||
* the keytab.A configuration error will occur.
|
||||
* the keytab, or from the shared state. A configuration error will occur.
|
||||
* <ul>
|
||||
* <p> <code>keyTab</code> = < filename > <code>doNotPrompt</code>=true ;
|
||||
* <p> <code>keyTab</code> = <filename> <code>doNotPrompt</code>=true ;
|
||||
* </ul>
|
||||
* <p>This is an illegal combination since useKeyTab is not set to true and
|
||||
* the keyTab is set. A configuration error will occur.
|
||||
@ -260,7 +277,7 @@ import sun.misc.HexDumpEncoder;
|
||||
* with the principal and TGT. If the TGT is not available,
|
||||
* do not prompt the user, instead fail the authentication.
|
||||
* <ul>
|
||||
* <p><code>principal</code>=< name ><code>useTicketCache</code> = true
|
||||
* <p><code>principal</code>=<name><code>useTicketCache</code> = true
|
||||
* <code>doNotPrompt</code>=true;
|
||||
*</ul>
|
||||
* <p> Get the TGT from the default cache for the principal and populate the
|
||||
@ -269,9 +286,9 @@ import sun.misc.HexDumpEncoder;
|
||||
* authentication will fail.
|
||||
* <ul>
|
||||
* <p> <code>useTicketCache</code> = true
|
||||
* <code>ticketCache</code>=< file name ><code>useKeyTab</code> = true
|
||||
* <code> keyTab</code>=< keytab filename >
|
||||
* <code>principal</code> = < principal name >
|
||||
* <code>ticketCache</code>=<file name><code>useKeyTab</code> = true
|
||||
* <code> keyTab</code>=<keytab filename>
|
||||
* <code>principal</code> = <principal name>
|
||||
* <code>doNotPrompt</code>=true;
|
||||
*</ul>
|
||||
* <p> Search the cache for the principal's TGT. If it is not available
|
||||
@ -281,7 +298,7 @@ import sun.misc.HexDumpEncoder;
|
||||
* If the key is not available or valid then authentication will fail.
|
||||
* <ul>
|
||||
* <p><code>useTicketCache</code> = true
|
||||
* <code>ticketCache</code>=< file name >
|
||||
* <code>ticketCache</code>=<file name>
|
||||
*</ul>
|
||||
* <p> The TGT will be obtained from the cache specified.
|
||||
* The Kerberos principal name used will be the principal name in
|
||||
@ -292,8 +309,8 @@ import sun.misc.HexDumpEncoder;
|
||||
* The Subject will be populated with the TGT.
|
||||
*<ul>
|
||||
* <p> <code>useKeyTab</code> = true
|
||||
* <code>keyTab</code>=< keytab filename >
|
||||
* <code>principal</code>= < principal name >
|
||||
* <code>keyTab</code>=<keytab filename>
|
||||
* <code>principal</code>= <principal name>
|
||||
* <code>storeKey</code>=true;
|
||||
*</ul>
|
||||
* <p> The key for the principal will be retrieved from the keytab.
|
||||
@ -303,7 +320,7 @@ import sun.misc.HexDumpEncoder;
|
||||
* password entered.
|
||||
* <ul>
|
||||
* <p> <code>useKeyTab</code> = true
|
||||
* <code>keyTab</code>=< keytabname >
|
||||
* <code>keyTab</code>=<keytabname>
|
||||
* <code>storeKey</code>=true
|
||||
* <code>doNotPrompt</code>=true;
|
||||
*</ul>
|
||||
@ -316,21 +333,23 @@ import sun.misc.HexDumpEncoder;
|
||||
* Subject's private credentials set. Otherwise the authentication will
|
||||
* fail.
|
||||
*<ul>
|
||||
* <p><code>useKeyTab</code> = true
|
||||
* <code>keyTab</code>=< file name > <code>storeKey</code>=true
|
||||
* <code>principal</code>= < principal name >
|
||||
* <p>
|
||||
* <code>useTicketCache</code>=true
|
||||
* <code>ticketCache</code>=< file name >;
|
||||
* <code>ticketCache</code>=<file name>;
|
||||
* <code>useKeyTab</code> = true
|
||||
* <code>keyTab</code>=<file name> <code>storeKey</code>=true
|
||||
* <code>principal</code>= <principal name>
|
||||
*</ul>
|
||||
* <p>The principal's key will be retrieved from the keytab and added
|
||||
* to the <code>Subject</code>'s private credentials. If the key
|
||||
* is not available, the
|
||||
* user will be prompted for the password; the key derived from the password
|
||||
* will be added to the Subject's private credentials set. The
|
||||
* client's TGT will be retrieved from the ticket cache and added to the
|
||||
* <p>
|
||||
* The client's TGT will be retrieved from the ticket cache and added to the
|
||||
* <code>Subject</code>'s private credentials. If the TGT is not available
|
||||
* in the ticket cache, it will be obtained using the authentication
|
||||
* in the ticket cache, or the TGT's client name does not match the principal
|
||||
* name, Java will use a secret key to obtain the TGT using the authentication
|
||||
* exchange and added to the Subject's private credentials.
|
||||
* This secret key will be first retrieved from the keytab. If the key
|
||||
* is not available, the user will be prompted for the password. In either
|
||||
* case, the key derived from the password will be added to the
|
||||
* Subject's private credentials set.
|
||||
* <ul>
|
||||
* <p><code>isInitiator</code> = false
|
||||
*</ul>
|
||||
@ -856,11 +875,13 @@ public class Krb5LoginModule implements LoginModule {
|
||||
}
|
||||
|
||||
private void validateConfiguration() throws LoginException {
|
||||
if (doNotPrompt && !useTicketCache && !useKeyTab)
|
||||
if (doNotPrompt && !useTicketCache && !useKeyTab
|
||||
&& !tryFirstPass && !useFirstPass)
|
||||
throw new LoginException
|
||||
("Configuration Error"
|
||||
+ " - either doNotPrompt should be "
|
||||
+ " false or useTicketCache/useKeyTab "
|
||||
+ " false or at least one of useTicketCache, "
|
||||
+ " useKeyTab, tryFirstPass and useFirstPass"
|
||||
+ " should be true");
|
||||
if (ticketCacheName != null && !useTicketCache)
|
||||
throw new LoginException
|
||||
@ -872,11 +893,12 @@ public class Krb5LoginModule implements LoginModule {
|
||||
throw new LoginException
|
||||
("Configuration Error - useKeyTab should be set to true "
|
||||
+ "to use the keytab" + keyTabName);
|
||||
if (storeKey && doNotPrompt && !useKeyTab)
|
||||
if (storeKey && doNotPrompt && !useKeyTab
|
||||
&& !tryFirstPass && !useFirstPass)
|
||||
throw new LoginException
|
||||
("Configuration Error - either doNotPrompt "
|
||||
+ "should be set to false or "
|
||||
+ "useKeyTab must be set to true for storeKey option");
|
||||
("Configuration Error - either doNotPrompt should be set to "
|
||||
+ " false or at least one of tryFirstPass, useFirstPass "
|
||||
+ "or useKeyTab must be set to true for storeKey option");
|
||||
if (renewTGT && !useTicketCache)
|
||||
throw new LoginException
|
||||
("Configuration Error"
|
||||
|
@ -109,13 +109,22 @@ public class Context {
|
||||
out.s = new Subject();
|
||||
Krb5LoginModule krb5 = new Krb5LoginModule();
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("tryFirstPass", "true");
|
||||
Map<String, Object> shared = new HashMap<String, Object>();
|
||||
|
||||
if (pass != null) {
|
||||
map.put("useFirstPass", "true");
|
||||
shared.put("javax.security.auth.login.name", user);
|
||||
shared.put("javax.security.auth.login.password", pass);
|
||||
} else {
|
||||
map.put("doNotPrompt", "true");
|
||||
map.put("useTicketCache", "true");
|
||||
if (user != null) {
|
||||
map.put("principal", user);
|
||||
}
|
||||
}
|
||||
if (storeKey) {
|
||||
map.put("storeKey", "true");
|
||||
}
|
||||
Map<String, Object> shared = new HashMap<String, Object>();
|
||||
shared.put("javax.security.auth.login.name", user);
|
||||
shared.put("javax.security.auth.login.password", pass);
|
||||
|
||||
krb5.initialize(out.s, null, shared, map);
|
||||
krb5.login();
|
||||
|
@ -32,6 +32,7 @@ import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import sun.security.krb5.*;
|
||||
import sun.security.krb5.internal.*;
|
||||
import sun.security.krb5.internal.ccache.CredentialsCache;
|
||||
import sun.security.krb5.internal.crypto.KeyUsage;
|
||||
import sun.security.krb5.internal.ktab.KeyTab;
|
||||
import sun.security.util.DerInputStream;
|
||||
@ -765,7 +766,29 @@ public class KDC {
|
||||
DerOutputStream out = new DerOutputStream();
|
||||
out.write(DerValue.createTag(DerValue.TAG_APPLICATION,
|
||||
true, (byte)Krb5.KRB_AS_REP), asRep.asn1Encode());
|
||||
return out.toByteArray();
|
||||
byte[] result = out.toByteArray();
|
||||
|
||||
// Added feature:
|
||||
// Write the current issuing TGT into a ccache file specified
|
||||
// by the system property below.
|
||||
String ccache = System.getProperty("test.kdc.save.ccache");
|
||||
if (ccache != null) {
|
||||
asRep.encKDCRepPart = enc_part;
|
||||
sun.security.krb5.internal.ccache.Credentials credentials =
|
||||
new sun.security.krb5.internal.ccache.Credentials(asRep);
|
||||
asReq.reqBody.cname.setRealm(getRealm());
|
||||
CredentialsCache cache =
|
||||
CredentialsCache.create(asReq.reqBody.cname, ccache);
|
||||
if (cache == null) {
|
||||
throw new IOException("Unable to create the cache file " +
|
||||
ccache);
|
||||
}
|
||||
cache.update(credentials);
|
||||
cache.save();
|
||||
new File(ccache).deleteOnExit();
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (KrbException ke) {
|
||||
ke.printStackTrace(System.out);
|
||||
KRBError kerr = ke.getError();
|
||||
|
184
jdk/test/sun/security/krb5/auto/LoginModuleOptions.java
Normal file
184
jdk/test/sun/security/krb5/auto/LoginModuleOptions.java
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6765491
|
||||
* @summary Krb5LoginModule a little too restrictive, and the doc is not clear.
|
||||
*/
|
||||
import com.sun.security.auth.module.Krb5LoginModule;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.security.auth.Subject;
|
||||
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;
|
||||
|
||||
public class LoginModuleOptions {
|
||||
|
||||
private static final String NAME = "javax.security.auth.login.name";
|
||||
private static final String PWD = "javax.security.auth.login.password";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
OneKDC kdc = new OneKDC(null);
|
||||
kdc.addPrincipal("foo", "bar".toCharArray());
|
||||
kdc.writeKtab(OneKDC.KTAB); // rewrite to add foo
|
||||
|
||||
// All 4 works: keytab, shared state, callback, cache
|
||||
login(null, "useKeyTab", "true", "principal", "dummy");
|
||||
login(null, "tryFirstPass", "true", NAME, OneKDC.USER,
|
||||
PWD, OneKDC.PASS);
|
||||
System.setProperty("test.kdc.save.ccache", "krbcc");
|
||||
login(new MyCallback(OneKDC.USER, OneKDC.PASS)); // save the cache
|
||||
System.clearProperty("test.kdc.save.ccache");
|
||||
login(null, "useTicketCache", "true", "ticketCache", "krbcc");
|
||||
|
||||
// Fallbacks
|
||||
// 1. ccache -> keytab
|
||||
login(null, "useTicketCache", "true", "ticketCache", "krbcc_non_exists",
|
||||
"useKeyTab", "true", "principal", "dummy");
|
||||
// 2. keytab -> shared
|
||||
login(null, "useKeyTab", "true", "principal", "dummy",
|
||||
"keyTab", "ktab_non_exist",
|
||||
"tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
|
||||
// 3. shared -> callback
|
||||
// 3.1. useFirstPass, no callback
|
||||
boolean failed = false;
|
||||
try {
|
||||
login(new MyCallback(OneKDC.USER, OneKDC.PASS),
|
||||
"useFirstPass", "true",
|
||||
NAME, OneKDC.USER, PWD, "haha".toCharArray());
|
||||
} catch (Exception e) {
|
||||
failed = true;
|
||||
}
|
||||
if (!failed) {
|
||||
throw new Exception("useFirstPass should not fallback to callback");
|
||||
}
|
||||
// 3.2. tryFirstPass, has callback
|
||||
login(new MyCallback(OneKDC.USER, OneKDC.PASS),
|
||||
"tryFirstPass", "true",
|
||||
NAME, OneKDC.USER, PWD, "haha".toCharArray());
|
||||
|
||||
// Preferences of type
|
||||
// 1. ccache preferred to keytab
|
||||
login(new MyCallback("foo", null),
|
||||
"useTicketCache", "true", "ticketCache", "krbcc",
|
||||
"useKeyTab", "true");
|
||||
// 2. keytab preferred to shared. This test case is not exactly correct,
|
||||
// because principal=dummy would shadow the PWD setting in the shared
|
||||
// state. So by only looking at the final authentication user name
|
||||
// (which is how this program does), there's no way to tell if keyTab
|
||||
// is picked first, or shared is tried first but fallback to keytab.
|
||||
login(null, "useKeyTab", "true", "principal", "dummy",
|
||||
"tryFirstPass", "true", NAME, "foo", PWD, "bar".toCharArray());
|
||||
// 3. shared preferred to callback
|
||||
login(new MyCallback("foo", "bar".toCharArray()),
|
||||
"tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
|
||||
|
||||
// Preferences of username
|
||||
// 1. principal preferred to NAME (NAME can be wrong or missing)
|
||||
login(null, "principal", OneKDC.USER,
|
||||
"tryFirstPass", "true", NAME, "someone_else", PWD, OneKDC.PASS);
|
||||
login(null, "principal", OneKDC.USER,
|
||||
"tryFirstPass", "true", PWD, OneKDC.PASS);
|
||||
// 2. NAME preferred to callback
|
||||
login(new MyCallback("someone_else", OneKDC.PASS),
|
||||
"principal", OneKDC.USER);
|
||||
// 3. With tryFirstPass, NAME preferred to callback
|
||||
login(new MyCallback("someone_else", null),
|
||||
"tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
|
||||
// 3.1. you must provide a NAME (when there's no principal)
|
||||
failed = false;
|
||||
try {
|
||||
login(new MyCallback(OneKDC.USER, null),
|
||||
"tryFirstPass", "true", PWD, OneKDC.PASS);
|
||||
} catch (Exception e) {
|
||||
failed = true;
|
||||
}
|
||||
if (!failed) {
|
||||
throw new Exception("useFirstPass must provide a NAME");
|
||||
}
|
||||
// 3.2 Hybrid, you can use NAME as "", and provide it using callback.
|
||||
// I don't think this is designed.
|
||||
login(new MyCallback(OneKDC.USER, null),
|
||||
"tryFirstPass", "true", NAME, "", PWD, OneKDC.PASS);
|
||||
|
||||
// Test for the bug fix: doNotPrompt can be true if tryFirstPass=true
|
||||
login(null, "doNotPrompt", "true", "storeKey", "true",
|
||||
"tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS);
|
||||
}
|
||||
|
||||
static void login(CallbackHandler callback, Object... options)
|
||||
throws Exception {
|
||||
Krb5LoginModule krb5 = new Krb5LoginModule();
|
||||
Subject subject = new Subject();
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
Map<String, Object> shared = new HashMap<String, Object>();
|
||||
|
||||
int count = options.length / 2;
|
||||
for (int i = 0; i < count; i++) {
|
||||
String key = (String) options[2 * i];
|
||||
Object value = options[2 * i + 1];
|
||||
if (key.startsWith("javax")) {
|
||||
shared.put(key, value);
|
||||
} else {
|
||||
map.put(key, (String) value);
|
||||
}
|
||||
}
|
||||
krb5.initialize(subject, callback, shared, map);
|
||||
krb5.login();
|
||||
krb5.commit();
|
||||
if (!subject.getPrincipals().iterator().next()
|
||||
.getName().startsWith(OneKDC.USER)) {
|
||||
throw new Exception("The authenticated is not " + OneKDC.USER);
|
||||
}
|
||||
}
|
||||
|
||||
static class MyCallback implements CallbackHandler {
|
||||
|
||||
private String name;
|
||||
private char[] password;
|
||||
|
||||
public MyCallback(String name, char[] password) {
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void handle(Callback[] callbacks) {
|
||||
for (Callback callback : callbacks) {
|
||||
System.err.println(callback);
|
||||
if (callback instanceof NameCallback) {
|
||||
System.err.println("name is " + name);
|
||||
((NameCallback) callback).setName(name);
|
||||
}
|
||||
if (callback instanceof PasswordCallback) {
|
||||
System.err.println("pass is " + new String(password));
|
||||
((PasswordCallback) callback).setPassword(password);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user