经过几天研究,终于跑起来了第一个JAAS例子。把代码贴上来,免得以后忘了。
MyCallbackHandler.java(处理用户输入)
package com.pingan.jaas.tutorial;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackInputStream;
import java.util.Arrays;
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.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
public class MyCallbackHandler implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof TextOutputCallback) {
// 根据指定的类型显示信息
TextOutputCallback toc = (TextOutputCallback) callbacks[i];
switch (toc.getMessageType()) {
case TextOutputCallback.INFORMATION:
System.out.println(toc.getMessage());
break;
case TextOutputCallback.ERROR:
System.out.println("ERROR: " + toc.getMessage());
break;
case TextOutputCallback.WARNING:
System.out.println("WARNING: " + toc.getMessage());
break;
default:
throw new IOException("Unsupported message type: "
+ toc.getMessageType());
}
} else if (callbacks[i] instanceof NameCallback) {
// 如果命令行没有提供用户名,提示用户输入用户名
NameCallback nc = (NameCallback) callbacks[i];
System.err.print(nc.getPrompt());
System.err.flush();
nc
.setName((new BufferedReader(new InputStreamReader(
System.in))).readLine());
} else if (callbacks[i] instanceof PasswordCallback) {
// prompt the user for a password
PasswordCallback pc = (PasswordCallback) callbacks[i];
System.err.print(pc.getPrompt());
System.err.flush();
pc.setPassword(readPassword(System.in));
} else {
throw new UnsupportedCallbackException(callbacks[i],
"Unrecognized Callback");
}
}
}
private char[] readPassword(InputStream in) throws IOException {
char[] lineBuffer;
char[] buf;
buf = lineBuffer = new char[128];
int room = buf.length;
int offset = 0;
int c;
loop: while (true) {
switch (c = in.read()) {
case -1:
case '\n':
break loop;
case '\r':
int c2 = in.read();
if ((c2 != '\n') && (c2 != -1)) {
if (!(in instanceof PushbackInputStream)) {
in = new PushbackInputStream(in);
}
((PushbackInputStream) in).unread(c2);
} else
break loop;
default:
if (--room < 0) {
buf = new char[offset + 128];
room = buf.length - offset - 1;
System.arraycopy(lineBuffer, 0, buf, 0, offset);
Arrays.fill(lineBuffer, ' ');
lineBuffer = buf;
}
buf[offset++] = (char) c;
break;
}
}
if (offset == 0) {
return null;
}
char[] ret = new char[offset];
System.arraycopy(buf, 0, ret, 0, offset);
Arrays.fill(buf, ' ');
return ret;
}
}
SampleAcn.java(客户端)
package com.pingan.jaas.tutorial;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
public class SampleAcn {
public static void main(String[] args) {
LoginContext lc = null;
try {
lc = new LoginContext("test5", new MyCallbackHandler());
} catch (LoginException le) {
System.err
.println("Cannot create LoginContext. " + le.getMessage());
System.exit(-1);
} catch (SecurityException se) {
System.err
.println("Cannot create LoginContext. " + se.getMessage());
System.exit(-1);
}
// 给用户3次登录机会
int retryTimes = 3;
int i;
for (i = 0; i < retryTimes; i++) {
try {
// attempt authentication
lc.login();
// if we return with no exception,
// authentication succeeded
break;
} catch (LoginException le) {
System.err.println("Authentication failed:");
System.err.println(" " + le.getMessage());
try {
Thread.sleep(3000);
} catch (Exception e) {
// ignore
}
}
}
// did they fail three times?
if (i == retryTimes) {
System.out.println("Sorry");
System.exit(-1);
}
System.out.println("Authentication succeeded!");
}
}
SamplePrincipal.java(自定义Principal类)
package com.pingan.jaas.tutorial;
import java.security.Principal;
public class SamplePrincipal implements Principal, java.io.Serializable {
private static final long serialVersionUID = -5974294088673749367L;
private String name;
public SamplePrincipal(String name) {
if (name == null)
throw new NullPointerException("illegal null input");
this.name = name;
}
public String getName() {
return name;
}
public String toString() {
return ("SamplePrincipal: " + name);
}
public boolean equals(Object o) {
if (o == null)
return false;
if (this == o)
return true;
if (!(o instanceof SamplePrincipal))
return false;
SamplePrincipal that = (SamplePrincipal) o;
if (this.getName().equals(that.getName()))
return true;
return false;
}
public int hashCode() {
return name.hashCode();
}
}
SampleLoginModule.java(自定义登录模块)
package com.pingan.jaas.tutorial;
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;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import examples.util.LogHelper;
public class SampleLoginModule {
// initial state
private Subject subject;
private CallbackHandler callbackHandler;
private Map sharedState;
private Map options;
// configurable option
private boolean debug = false;
// the authentication status
private boolean succeeded = false;
private boolean commitSucceeded = false;
// username and password
private String username;
private char[] password;
// testUser's SamplePrincipal
private SamplePrincipal userPrincipal;
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map sharedState, Map options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
// initialize any configured options
debug = "true".equalsIgnoreCase((String) options.get("debug"));
}
public boolean login() throws LoginException {
// prompt for a user name and password
if (callbackHandler == null)
throw new LoginException("Error: no CallbackHandler available "
+ "to garner authentication information from the user");
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("user name: ");
callbacks[1] = new PasswordCallback("password: ", false);
try {
callbackHandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
char[] tmpPassword = ((PasswordCallback) callbacks[1])
.getPassword();
if (tmpPassword == null) {
// treat a NULL password as an empty password
tmpPassword = new char[0];
}
password = new char[tmpPassword.length];
System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length);
((PasswordCallback) callbacks[1]).clearPassword();
} catch (java.io.IOException ioe) {
throw new LoginException(ioe.toString());
} catch (UnsupportedCallbackException uce) {
throw new LoginException("Error: " + uce.getCallback().toString()
+ " not available to garner authentication information "
+ "from the user");
}
// print debugging information
if (debug) {
System.out.println("\t\t[SampleLoginModule] "
+ "user entered user name: " + username);
System.out.print("\t\t[SampleLoginModule] "
+ "user entered password: ");
for (int i = 0; i < password.length; i++)
System.out.print(password[i]);
System.out.println();
}
// 校验用户名和密码(用户名为testUser,密码为testPassword,区分大小写)
boolean usernameCorrect = false;
boolean passwordCorrect = false;
if (username.equals("testUser"))
usernameCorrect = true;
if (usernameCorrect && password.length == 12 && password[0] == 't'
&& password[1] == 'e' && password[2] == 's'
&& password[3] == 't' && password[4] == 'P'
&& password[5] == 'a' && password[6] == 's'
&& password[7] == 's' && password[8] == 'w'
&& password[9] == 'o' && password[10] == 'r'
&& password[11] == 'd') {
// authentication succeeded!!!
passwordCorrect = true;
if (debug)
System.out.println("\t\t[SampleLoginModule] "
+ "authentication succeeded");
succeeded = true;
return true;
} else {
// authentication failed -- clean out state
if (debug)
System.out.println("\t\t[SampleLoginModule] "
+ "authentication failed");
succeeded = false;
username = null;
for (int i = 0; i < password.length; i++)
password[i] = ' ';
password = null;
if (!usernameCorrect) {
throw new FailedLoginException("User Name Incorrect");
} else {
throw new FailedLoginException("Password Incorrect");
}
}
}
public boolean commit() throws LoginException {
LogHelper.log2Console("succeeded=" + succeeded);
if (succeeded == false) {
return false;
} else {
// add a Principal (authenticated identity)
// to the Subject
// assume the user we authenticated is the SamplePrincipal
userPrincipal = new SamplePrincipal(username);
if (!subject.getPrincipals().contains(userPrincipal))
subject.getPrincipals().add(userPrincipal);
if (debug) {
System.out.println("\t\t[SampleLoginModule] "
+ "added SamplePrincipal to Subject");
}
// in any case, clean out state
username = null;
for (int i = 0; i < password.length; i++)
password[i] = ' ';
password = null;
commitSucceeded = true;
return true;
}
}
public boolean abort() throws LoginException {
LogHelper.log2Console("succeeded=" + succeeded);
if (succeeded == false) {
return false;
} else if (succeeded == true && commitSucceeded == false) {
// login succeeded but overall authentication failed
succeeded = false;
username = null;
if (password != null) {
for (int i = 0; i < password.length; i++)
password[i] = ' ';
password = null;
}
userPrincipal = null;
} else {
// overall authentication succeeded and commit succeeded,
// but someone else's commit failed
logout();
}
return true;
}
public boolean logout() throws LoginException {
subject.getPrincipals().remove(userPrincipal);
succeeded = false;
succeeded = commitSucceeded;
username = null;
if (password != null) {
for (int i = 0; i < password.length; i++)
password[i] = ' ';
password = null;
}
userPrincipal = null;
return true;
}
}
该类没有实现javax.security.auth.spi.LoginModule接口,似乎也可以运行。为简单起见,代码里面用户名固定为testUser,密码固定为testPassword。
还有一个MyLoginModule类,跟SampleLoginModule类是一样的,只是修改了硬编码的用户名和密码,便于测试。
Sample_jaas.config(JAAS配置文件)
Sample {
com.pingan.jaas.tutorial.SampleLoginModule required debug=true;
};
Sample2 {
com.pingan.jaas.tutorial.MyLoginModule required debug=true;
};
all_required {
com.pingan.jaas.tutorial.SampleLoginModule required debug=true;
com.pingan.jaas.tutorial.MyLoginModule required debug=true;
};
test3 {
com.pingan.jaas.tutorial.SampleLoginModule required debug=true;
com.pingan.jaas.tutorial.MyLoginModule Sufficient debug=true;
};
test4 {
com.pingan.jaas.tutorial.MyLoginModule Sufficient debug=true;
com.pingan.jaas.tutorial.SampleLoginModule required debug=true;
};
test5 {
com.pingan.jaas.tutorial.MyLoginModule Optional debug=true;
com.pingan.jaas.tutorial.SampleLoginModule required debug=true;
};
为了测试不同情况,配置了多个组合,具体使用哪个组合,在上文中客户端代码指定。
编译后运行客户端,需要在命令行指定JAAS配置文件位置,参考命令行如下:
java -Djava.security.auth.login.config=com/pingan/jaas/tutorial/sample_jaas.config -cp ".;%CLASSPATH%;C:\bea\weblogic81\server\lib\weblogic.jar;C:\bea\weblogic81\server\lib\mbeantypes\wlManagement.jar;" com.pingan.jaas.tutorial.SampleAcn
运行结果:
C:\bea\user_projects\applications\Test3\Test3Web\WEB-INF\classes>java -Djava.security.auth.login.config=com/pingan/jaas/tutorial/sample_jaas.config -cp ".;%CLASSPATH%;C:\bea\weblogic81\server\lib\weblogic.jar;C:\bea\weblogic81\server\lib\mbeantypes\wlManagement.jar;" com.pingan.jaas.tutorial.SampleAcn
user name: testUser
password: testPassword
[MyLoginModule] 用户输入的用户名: testUser
[MyLoginModule] 用户输入的密码:testPassword
[MyLoginModule] authentication failed
user name: testUser
password: testPassword
[SampleLoginModule] user entered user name: testUser
[SampleLoginModule] user entered password: testPassword
[SampleLoginModule] authentication succeeded
com.pingan.jaas.tutorial.MyLoginModule.commit(131)
---------------------------------- <2011-02-14 10:03:14> ----------------------------------
succeeded=false
---------------------------------- <2011-02-14 10:03:14> ----------------------------------
com.pingan.jaas.tutorial.SampleLoginModule.commit(131)
---------------------------------- <2011-02-14 10:03:14> ----------------------------------
succeeded=true
---------------------------------- <2011-02-14 10:03:14> ----------------------------------
[SampleLoginModule] added SamplePrincipal to Subject
Authentication succeeded!
还有一个帮助类LogHelper.java:
package examples.util;
import java.text.SimpleDateFormat;
import java.util.Date;
public class LogHelper {
private static final String 换行符 = "\r\n";
private static final String 分隔线 = "----------------------------------";
private static final SimpleDateFormat sf = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
public static void log2Console(String msg) {
StackTraceElement[] stackTraceElements = new Throwable()
.getStackTrace();
StackTraceElement stackTraceElement = stackTraceElements[1];
StringBuffer sb = new StringBuffer();
// sb.append(换行符);
sb.append(换行符).append(stackTraceElement.getClassName());
sb.append('.');
sb.append(stackTraceElement.getMethodName()).append("(");
sb.append(stackTraceElement.getLineNumber()).append(")");
sb.append(换行符);
String separateLine = buildSeparateLine();
sb.append(separateLine);
sb.append(msg).append(换行符);
sb.append(separateLine);
System.out.println(sb.toString());
}
public static void log2Console(String[] array) {
StackTraceElement[] stackTraceElements = new Throwable()
.getStackTrace();
StackTraceElement stackTraceElement = stackTraceElements[1];
StringBuffer sb = new StringBuffer();
// sb.append(换行符);
sb.append(换行符).append(stackTraceElement.getClassName());
sb.append('.');
sb.append(stackTraceElement.getMethodName()).append("(");
sb.append(stackTraceElement.getLineNumber()).append(")");
sb.append(换行符);
String separateLine = buildSeparateLine();
sb.append(separateLine);
if (array == null) {
sb.append("数组为空(NULL)").append(换行符);
} else {
sb.append('{');
for (int i = 0; i < array.length; i++) {
if (i >= 0) {
sb.append(", ");
}
sb.append('"').append(array[i]).append('"');
}
sb.append('}').append(换行符);
}
sb.append(separateLine);
System.out.println(sb.toString());
}
private static String buildSeparateLine() {
StringBuffer sb = new StringBuffer();
Date currentTime = new Date();
sb.append(分隔线).append(" <").append(sf.format(currentTime)).append("> ")
.append(分隔线).append(换行符);
return sb.toString();
}
}