JavaTM Authentication and Authorization Service (JAAS)在JavaTM 2 SDK Standard Edition (J2SDK)1.3版本中是一个可选的包,而现在JAAS已经整合到J2SDK1.4中. |
有两种情况使用JAAS: |
1.用来鉴定用户,能够可靠并安全的确定谁在执行java代码,而不管执行的是一个应用程序或小程序或bean或java servlet; |
2.授权用户并保证用户能有通路控制执行许可. |
JAAS实现一个java版本的standard Pluggable Authentication Module (PAM)的框架.传统上java提供一个原代码的通路控制(通路控制基于代码来源和谁签名).它所缺乏的是基于谁在运行代码的强制通路控制能力.JAAS提供了一个框架增加了安全性来支持这种功能. |
JAAS是一种可插入的风格.这可以使应用程序对鉴定技术保持独立性.新的或更新的鉴定技术能够插入应用程序下面而不需要修改程序本身.应用程序可以例示一 个LoginContext对象依此提及一个确定鉴定技术的Configuration或用来执行鉴定的LoginModule来允许鉴定.LoginModule可以提示和检验用户名和用户密码.其他的也可以读出和检验一个声音或一个指纹. |
当用户或服务器执行的代码被鉴定以后,JAAS组件与java通路控制模式一起保护敏感资源的通路.不像J2SDK13或以上版本通路控制决定由代码位置和代码签名者,J2SDK14通路控制决定有运行代码的代码源和用户或服务器.假如鉴定成功,Subject被LoginModule用相关的Principals和信任书更新. |
从下一个例子中可以看出JAAS中Authentication的作用. |
1.SampleAcn.java |
package sample; |
import java.io.*; |
import java.util.*; |
import javax.security.auth.login.*; |
import javax.security.auth.*; |
import javax.security.auth.callback.*; |
//for a simple Authentication . |
public class SampleAcn { |
public static void main(String[] args) { |
LoginContext lc = null; |
try { |
/*为了鉴别用户,首先需要一个javax.security.auth.login.LoginContext. |
1.参数"Sample"是JAAS注册配置文件的入口名字.一个入口指定实现了 鉴别技术的类,这个类必须实现LoginModule(在javax.security.auth.spi中)接口; |
2.CallbackHandler的实例化.当一个LoginModule需要与用户通信,如请求用户名 |
或用户密码,它需要保持独立性因为用户通信有许多方式.LoginModule调用javax.security.auth.callback.CallbackHandler |
去执行与用户通信和获得请求信息包括用户名用户密码. |
*/ |
lc = new LoginContext("Sample", 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); |
} |
int i; |
for (i = 0; i < 3; i++) { |
try { |
/** |
* 调用LoginContext中login方法.LoginContext例式一个空的javax.security.auth.Subject对象, |
* (描述被鉴定的用户或服务器).login方法调用LoginModule中的方法执行注册和鉴定. |
* SampleLoginModule利用MyCallbackHandler获得用户名和用户密码.SampleLoginModule将 |
* 检查用户名和用户密码. |
*/ |
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.currentThread().sleep(3000); |
} catch (Exception e) { |
// ignore |
} |
} |
} |
// did they fail three times? |
if (i == 3) { |
System.out.println("Sorry"); |
System.exit(-1); |
} |
System.out.println("Authentication succeeded!"); |
} |
} |
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) { |
// display the message according to the specified type |
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) { |
// prompt the user for a username |
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 sensitive information |
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; |
int i; |
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; |
} |
} |
2. SampleLoginModule.java |
package sample.module; |
import java.util.*; |
import java.io.IOException; |
import javax.security.auth.*; |
import javax.security.auth.callback.*; |
import javax.security.auth.login.*; |
import javax.security.auth.spi.*; |
import sample.principal.SamplePrincipal; |
public class SampleLoginModule implements LoginModule { |
// initial state |
private Subject subject; |
private CallbackHandler callbackHandler; |
private Map sharedState; |
private Map options; |
// 配置选项. |
private boolean debug = false; |
//鉴别状况 |
private boolean succeeded = false; |
private boolean commitSucceeded = false; |
//用户名和用户密码. |
private String username; |
private char[] password; |
//用户的Principal |
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 { |
//提示用户名和用户密码. |
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"); |
} |
//打印调试信息. |
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') { |
//鉴别成功. |
passwordCorrect = true; |
if (debug) |
System.out.println("/t/t[SampleLoginModule] " + |
"authentication succeeded"); |
succeeded = true; |
return true; |
} else { |
//鉴别失败.--------清除输出状态. |
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 { |
if (succeeded == false) { |
return false; |
} else { |
//假设我们鉴别的用户是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"); |
} |
username = null; |
for (int i = 0; i < password.length; i++) |
password[i] = ' '; |
password = null; |
commitSucceeded = true; |
return true; |
} |
} |
public boolean abort() throws LoginException { |
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 { |
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; |
} |
} |
3. SamplePrincipal.java: |
package sample.principal; |
import java.security.Principal; |
public class SamplePrincipal implements Principal, java.io.Serializable { |
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(); |
} |
} |
4. 配置文件:sample_jaas.config: |
Sample { |
sample.module.SampleLoginModule required debug=true; |
}; |
编译SampleAcn.java ,SampleLoginModule.java,SamplePrincipal.java 到d:/temp(如:javac -d d:/temp SampleAcn.java SampleLoginModule.java,SamplePrincipal.java),然后将sample_jaas_config放入d:/temp目录,然后运行java -Djava.security.auth.login.config==sample_jaas.config sample.SampleAcn |
系统要求输入用户名和密码,输入:testUser 和testPassword,如果鉴别成功,将输出: |
user name: testUser |
password: testPassword |
[SampleLoginModule] user entered user name: testUser |
[SampleLoginModule] user entered password: testPassword |
[SampleLoginModule] authentication succeeded |
[SampleLoginModule] added SamplePrincipal to Subject |
Authentication succeeded! |