原文:http://www.hibernate.org/139.html
译者:jredfox
Java认证和授权API(JAAS)为应用程序处理用户的认证和授权问题提供了标准方式。很多人在Unix/Linux系统中比较JAAS和PAM模块。
本文不对JAAS进行详细的讨论,但给大家简单的介绍一下。在JAAS架构中,当用户登录系统时,系统给用户一个Subject,Subject中包含有一个或多个Principal。每个Principal给用户提供身份验证,例如用户ID,也可以验证组和角色。Subject也包含公有的和私有的许可证(credential),例如X.509证书、私钥等。JAAS通过公开的Permission进行授权,Permission是在外部Policy文件里进行维护的。本文也不讨论授权。就像下面所讨论的,实际的登录过程是由LoginModule来处理的。如果你想了解更多的信息,这里有相关JAAS的详细介绍:http://www.javaworld.com/javaworld/jw-09-2002/jw-0913-jaas.html。
实现Hibernate JAAS LoginModule的第一步是定义一个或多个Principal。下面是一个典型的实例,但是记住你能把你想要的任何事物――用户ID,E-mail地址,电话号码,公钥――填进Principal中是很重要的,这些事物都能在持久化的User对象中发现。
final public class HibernatePrincipal implements Principal {
private String name;
public HibernatePrincipal() {
name = "";
}
public HibernatePrincipal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int hashCode() {
return name.hashCode();
}
public boolean equals(Object o) {
if (!(o instanceof HibernatePrincipal)) {
return false;
}
return name.equals(((HibernatePrincipal) o).name);
}
public String toString() {
return name;
}
}
N.B.,你必须小心防止恶意用户通过继承你的类来获取他们所需要的许可证(credential)。解决上述问题的方法一是你可以声明你的Principal是final,二是你总是检查许可证(credential)是否是扩展类型。
public void foo(Principal p) {
// DO NOT USE THIS
if (p instanceof HibernatePrincipal) {
...
}
// use this instead
if (p.getClass().equals(HibernatePrincipal.class)) {
...
}
// or even this
if (p.getClass().getName().equals(HibernatePrincipal.getClass().getName()) {
...
}
}
}
本文也不详细讨论许可证文件(credential),但User属性映射到Subject许可证是很容易的。例如,用户名可以作为公开许可证的一个合理候选。
既然有了Principal,我们可以为JAAS LoginModule包装一些标准的Hibernate代码。
/**
* HibernateLoginModule is a LoginModule that authenticates
* a given username/password credential against a Hibernate
* session.
*
* @see javax.security.auth.spi.LoginModule
*/
public class HibernateLoginModule implements LoginModule {
// initial state
CallbackHandler handler;
Subject subject;
Map sharedState;
Map options;
Digest digest;
// temporary state
Vector principals;
// authentication status
boolean success;
// configurable options
boolean debug;
/** Hibernate session factory */
SessionFactory sf = null;
/** Hibernate query */
private static final String query =
"from u in class " + User.class + " where u.name=?";
public HibernateLoginModule() {
credentials = new Vector();
principals = new Vector();
success = false;
debug = false;
}
/**
* Initialize our state.
*/
public void initialize (Subject subject, CallbackHandler handler,
Map sharedState, Map options) {
this.handler = handler;
this.subject = subject;
this.sharedState = sharedState;
this.options = options;
if (options.containsKey("debug")) {
debug = "true".equalsIgnoreCase((String) options.get("debug"));
}
if (options.containsKey("digest")) {
digest = new Digest((String) options.get("digest"));
} else {
digest = new Digest();
}
// elided: standard code to get Hibernate =SessionFactory=.
}
/**
* First phase of login process.
*/
public boolean login() throws LoginException {
if (handler == null) {
throw new LoginException("Error: no CallbackHandler available");
}
try {
Callback[] callbacks = new Callback[] {
new NameCallback("User: "),
new PasswordCallback("Password: ", false)
};
handler.handle(callbacks);
String username = ((NameCallback) callbacks[0]).getName();
char[] password = ((PasswordCallback) callbacks[1]).getPassword();
((PasswordCallback) callbacks[1]).clearPassword();
success = validate(username, password);
callbacks[0] = null;
callbacks[1] = null;
if (!success) {
throw new LoginException("Authentication failed: Password does not match");
}
return true;
} catch (LoginException e) {
throw e;
} catch (Exception e) {
success = false;
throw new LoginException(e.getMessage());
}
}
/**
* Second phase of login - by now we know user is authenticated
* and we just need to update the subject.
*/
public boolean commit() throws LoginException {
if (success) {
if (subject.isReadOnly()) {
throw new LoginException("Subject is read-only");
}
try {
Iterator i = principals.iterator();
subject.getPrincipals().addAll(principals);
principals.clear();
return true;
} catch (Exception e) {
throw new LoginException(e.getMessage());
}
} else {
principals.clear();
}
return true;
}
/**
* Second phase - somebody else rejected user so we need to
* clear our state.
*/
public boolean abort() throws LoginException {
success = false;
logout();
return true;
}
/**
* User is logging out - clear our information from the subject.
*/
public boolean logout() throws LoginException {
principals.clear();
// remove the principals the login module added
Iterator i = subject.getPrincipals(HibernatePrincipal.class).iterator();
while (i.hasNext()) {
HibernatePrincipal p = (HibernatePrincipal) i.next();
subject.getPrincipals().remove(p);
}
return true;
}
/**
* Validate the user name and password. This is the Hibernate-specific
* code.
*/
private boolean validate(String username, char[] password) throws Exception {
boolean valid = false;
List users = null;
Session s = null;
try {
s = sf.openSession();
users = (List) s.find(query, username, Hibernate.STRING);
} catch (Exception e) {
} finally {
if (s != null) {
try { s.close(); } catch (HibernateException e) { }
}
}
// are there no matching records?...
if (users == null || users.size() == 0) {
return false;
}
// compare passwords...
User user = (User) users.get(0);
String hash = user.getPassword();
if (hash != null && password != null && password.length > 0) {
valid = hash.equals(digest.digest(new String(password)));
}
if (valid) {
this.principals.add(new HibernatePrincipal(user.getId(),
user.getName()));
}
return valid;
}
}
例子中,我们利用了Tomcat类库中密码digest功能(password digest function)(你要为HexUtils类载入catalina.jar文件)。
import org.apache.catalina.util.HexUtils;
/**
* Quick and dirty password digest function. The HexUtils class
* comes from the Tomcat catalina.jar.
*/
public class Digest {
static MessageDigest md = null;
public Digest() {
this("MD5");
}
public Digest(String digest) {
try {
md = MessageDigest.getInstance(digest);
} catch (NoSuchAlgorithmException e) {
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) { }
}
}
/**
* Digest function from Tomcat.
*/
public String digest(String credentials) {
if (md == null) {
return credentials;
}
synchronized (this) {
try {
md.reset();
md.update(credentials.getBytes());
return (HexUtils.convert(md.digest()));
} catch (Exception e) {
return credentials;
}
}
}
}
最后一步是为我们的应用程序配置我们的Hibernate登录模块。我们先创建JAAS配置文件,然后通过java.security.auth.login.config参数传给应用程序。在这个例子里我们定义JAAS属性“Example”。
Example {
HibernateLoginModule required debug="true";
};
现在通过我们Hibernate模块可以认证任何基于JAAS的应用程序了。下面是简单的测试程序:
/**
* simple CallbackHandler suitable for testing purposes
*/
public static class Handler implements CallbackHandler {
private Test t;
private String username;
private char[] credentials;
public Handler(Test t, String username, char[] credentials) {
super();
this.t = t;
this.username = username;
this.credentials = credentials;
}
public void handle(Callback callbacks[])
throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
((NameCallback) callbacks[i]).setName(username);
}
else if (callbacks[i] instanceof PasswordCallback) {
((PasswordCallback) callbacks[i]).setPassword(credentials);
} else {
throw new UnsupportedCallbackException(callbacks[i]);
}
}
}
}
/**
* Simple JAAS-aware application.
*/
public class Test {
LoginContext l = null;
/**
* attempt to log in as the user, returning the =Subject=
* if successful.
*/
public Subject login(String username, char[] credentials) {
try {
CallbackHandler cb = new Handler(this, username, credentials);
l = new LoginContext("Example", cb);
} catch (LoginException e) {
return null;
}
Subject subject = null;
try {
l.login();
subject = l.getSubject();
if (subject == null) {
return null;
}
} catch (AccountExpiredException e) {
} catch (CredentialExpiredException e) {
} catch (FailedLoginException e) {
} catch (LoginException e) {
}
return subject;
}
/**
* log out of application
*/
public void logout() {
if (l != null) {
try {
l.logout();
} catch (LoginException e) {
}
}
}
public static void main(String[] args) throws Exception {
Test t = new Test();
String username = "test";
String password = "test";
Subject subj = t.login(username, password.toCharArray());
if (subj != null) {
Iterator i = subj.getPrincipals(HibernatePrincipal.class).iterator();
while (i.hasNext()) {
HibernatePrincipal p = (HibernatePrincipal) i.next();
System.out.println("logged in as: " + p.getName());
}
t.logout();
}
else {
System.out.println("unable to log in as user");
}
}
}
正如上文间接提到的,JAAS的真正威力不在于它处理用户登录的灵活性,而是通过Permissions提供的公开的安全模块和处理许可证的机制。
译者:jredfox
Java认证和授权API(JAAS)为应用程序处理用户的认证和授权问题提供了标准方式。很多人在Unix/Linux系统中比较JAAS和PAM模块。
本文不对JAAS进行详细的讨论,但给大家简单的介绍一下。在JAAS架构中,当用户登录系统时,系统给用户一个Subject,Subject中包含有一个或多个Principal。每个Principal给用户提供身份验证,例如用户ID,也可以验证组和角色。Subject也包含公有的和私有的许可证(credential),例如X.509证书、私钥等。JAAS通过公开的Permission进行授权,Permission是在外部Policy文件里进行维护的。本文也不讨论授权。就像下面所讨论的,实际的登录过程是由LoginModule来处理的。如果你想了解更多的信息,这里有相关JAAS的详细介绍:http://www.javaworld.com/javaworld/jw-09-2002/jw-0913-jaas.html。
实现Hibernate JAAS LoginModule的第一步是定义一个或多个Principal。下面是一个典型的实例,但是记住你能把你想要的任何事物――用户ID,E-mail地址,电话号码,公钥――填进Principal中是很重要的,这些事物都能在持久化的User对象中发现。
final public class HibernatePrincipal implements Principal {
private String name;
public HibernatePrincipal() {
name = "";
}
public HibernatePrincipal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int hashCode() {
return name.hashCode();
}
public boolean equals(Object o) {
if (!(o instanceof HibernatePrincipal)) {
return false;
}
return name.equals(((HibernatePrincipal) o).name);
}
public String toString() {
return name;
}
}
N.B.,你必须小心防止恶意用户通过继承你的类来获取他们所需要的许可证(credential)。解决上述问题的方法一是你可以声明你的Principal是final,二是你总是检查许可证(credential)是否是扩展类型。
public void foo(Principal p) {
// DO NOT USE THIS
if (p instanceof HibernatePrincipal) {
...
}
// use this instead
if (p.getClass().equals(HibernatePrincipal.class)) {
...
}
// or even this
if (p.getClass().getName().equals(HibernatePrincipal.getClass().getName()) {
...
}
}
}
本文也不详细讨论许可证文件(credential),但User属性映射到Subject许可证是很容易的。例如,用户名可以作为公开许可证的一个合理候选。
既然有了Principal,我们可以为JAAS LoginModule包装一些标准的Hibernate代码。
/**
* HibernateLoginModule is a LoginModule that authenticates
* a given username/password credential against a Hibernate
* session.
*
* @see javax.security.auth.spi.LoginModule
*/
public class HibernateLoginModule implements LoginModule {
// initial state
CallbackHandler handler;
Subject subject;
Map sharedState;
Map options;
Digest digest;
// temporary state
Vector principals;
// authentication status
boolean success;
// configurable options
boolean debug;
/** Hibernate session factory */
SessionFactory sf = null;
/** Hibernate query */
private static final String query =
"from u in class " + User.class + " where u.name=?";
public HibernateLoginModule() {
credentials = new Vector();
principals = new Vector();
success = false;
debug = false;
}
/**
* Initialize our state.
*/
public void initialize (Subject subject, CallbackHandler handler,
Map sharedState, Map options) {
this.handler = handler;
this.subject = subject;
this.sharedState = sharedState;
this.options = options;
if (options.containsKey("debug")) {
debug = "true".equalsIgnoreCase((String) options.get("debug"));
}
if (options.containsKey("digest")) {
digest = new Digest((String) options.get("digest"));
} else {
digest = new Digest();
}
// elided: standard code to get Hibernate =SessionFactory=.
}
/**
* First phase of login process.
*/
public boolean login() throws LoginException {
if (handler == null) {
throw new LoginException("Error: no CallbackHandler available");
}
try {
Callback[] callbacks = new Callback[] {
new NameCallback("User: "),
new PasswordCallback("Password: ", false)
};
handler.handle(callbacks);
String username = ((NameCallback) callbacks[0]).getName();
char[] password = ((PasswordCallback) callbacks[1]).getPassword();
((PasswordCallback) callbacks[1]).clearPassword();
success = validate(username, password);
callbacks[0] = null;
callbacks[1] = null;
if (!success) {
throw new LoginException("Authentication failed: Password does not match");
}
return true;
} catch (LoginException e) {
throw e;
} catch (Exception e) {
success = false;
throw new LoginException(e.getMessage());
}
}
/**
* Second phase of login - by now we know user is authenticated
* and we just need to update the subject.
*/
public boolean commit() throws LoginException {
if (success) {
if (subject.isReadOnly()) {
throw new LoginException("Subject is read-only");
}
try {
Iterator i = principals.iterator();
subject.getPrincipals().addAll(principals);
principals.clear();
return true;
} catch (Exception e) {
throw new LoginException(e.getMessage());
}
} else {
principals.clear();
}
return true;
}
/**
* Second phase - somebody else rejected user so we need to
* clear our state.
*/
public boolean abort() throws LoginException {
success = false;
logout();
return true;
}
/**
* User is logging out - clear our information from the subject.
*/
public boolean logout() throws LoginException {
principals.clear();
// remove the principals the login module added
Iterator i = subject.getPrincipals(HibernatePrincipal.class).iterator();
while (i.hasNext()) {
HibernatePrincipal p = (HibernatePrincipal) i.next();
subject.getPrincipals().remove(p);
}
return true;
}
/**
* Validate the user name and password. This is the Hibernate-specific
* code.
*/
private boolean validate(String username, char[] password) throws Exception {
boolean valid = false;
List users = null;
Session s = null;
try {
s = sf.openSession();
users = (List) s.find(query, username, Hibernate.STRING);
} catch (Exception e) {
} finally {
if (s != null) {
try { s.close(); } catch (HibernateException e) { }
}
}
// are there no matching records?...
if (users == null || users.size() == 0) {
return false;
}
// compare passwords...
User user = (User) users.get(0);
String hash = user.getPassword();
if (hash != null && password != null && password.length > 0) {
valid = hash.equals(digest.digest(new String(password)));
}
if (valid) {
this.principals.add(new HibernatePrincipal(user.getId(),
user.getName()));
}
return valid;
}
}
例子中,我们利用了Tomcat类库中密码digest功能(password digest function)(你要为HexUtils类载入catalina.jar文件)。
import org.apache.catalina.util.HexUtils;
/**
* Quick and dirty password digest function. The HexUtils class
* comes from the Tomcat catalina.jar.
*/
public class Digest {
static MessageDigest md = null;
public Digest() {
this("MD5");
}
public Digest(String digest) {
try {
md = MessageDigest.getInstance(digest);
} catch (NoSuchAlgorithmException e) {
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) { }
}
}
/**
* Digest function from Tomcat.
*/
public String digest(String credentials) {
if (md == null) {
return credentials;
}
synchronized (this) {
try {
md.reset();
md.update(credentials.getBytes());
return (HexUtils.convert(md.digest()));
} catch (Exception e) {
return credentials;
}
}
}
}
最后一步是为我们的应用程序配置我们的Hibernate登录模块。我们先创建JAAS配置文件,然后通过java.security.auth.login.config参数传给应用程序。在这个例子里我们定义JAAS属性“Example”。
Example {
HibernateLoginModule required debug="true";
};
现在通过我们Hibernate模块可以认证任何基于JAAS的应用程序了。下面是简单的测试程序:
/**
* simple CallbackHandler suitable for testing purposes
*/
public static class Handler implements CallbackHandler {
private Test t;
private String username;
private char[] credentials;
public Handler(Test t, String username, char[] credentials) {
super();
this.t = t;
this.username = username;
this.credentials = credentials;
}
public void handle(Callback callbacks[])
throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
((NameCallback) callbacks[i]).setName(username);
}
else if (callbacks[i] instanceof PasswordCallback) {
((PasswordCallback) callbacks[i]).setPassword(credentials);
} else {
throw new UnsupportedCallbackException(callbacks[i]);
}
}
}
}
/**
* Simple JAAS-aware application.
*/
public class Test {
LoginContext l = null;
/**
* attempt to log in as the user, returning the =Subject=
* if successful.
*/
public Subject login(String username, char[] credentials) {
try {
CallbackHandler cb = new Handler(this, username, credentials);
l = new LoginContext("Example", cb);
} catch (LoginException e) {
return null;
}
Subject subject = null;
try {
l.login();
subject = l.getSubject();
if (subject == null) {
return null;
}
} catch (AccountExpiredException e) {
} catch (CredentialExpiredException e) {
} catch (FailedLoginException e) {
} catch (LoginException e) {
}
return subject;
}
/**
* log out of application
*/
public void logout() {
if (l != null) {
try {
l.logout();
} catch (LoginException e) {
}
}
}
public static void main(String[] args) throws Exception {
Test t = new Test();
String username = "test";
String password = "test";
Subject subj = t.login(username, password.toCharArray());
if (subj != null) {
Iterator i = subj.getPrincipals(HibernatePrincipal.class).iterator();
while (i.hasNext()) {
HibernatePrincipal p = (HibernatePrincipal) i.next();
System.out.println("logged in as: " + p.getName());
}
t.logout();
}
else {
System.out.println("unable to log in as user");
}
}
}
正如上文间接提到的,JAAS的真正威力不在于它处理用户登录的灵活性,而是通过Permissions提供的公开的安全模块和处理许可证的机制。
相关推荐
通常,Java提供了一些预定义的LoginModule,如`jaas.JDBCLoginModule`或`jaas.FileLoginModule`,但这些可能无法满足所有业务场景。因此,创建自定义LoginModule对于扩展和适应特定的认证和授权逻辑至关重要。 ...
Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全认证和授权的核心组件。它提供了一个框架,使得应用程序可以通过统一的方式来处理用户身份验证和权限控制。JAAS的主要目标是简化应用的...
相比于早期版本的Java安全模型,JAAS显著增强了安全性,通过引入“谁”这一概念来决定代码执行权限。 #### 二、JAAS的关键特性 - **可插拔的身份验证机制**:JAAS支持多种身份验证方法,并允许开发者根据实际需求...
与Java 2 Security规范相比,JAAS的引入使得开发者能够基于代码执行者的身份进行更细粒度的权限控制,解决了传统Java SE安全模型中无法基于执行者身份授权的问题。 #### 二、JAAS的关键概念 1. **Subject**:主体...
Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全性的核心组件,它为开发者提供了一种机制来处理用户身份验证和权限控制。这个服务允许应用程序执行基于角色的安全性,这意味着用户的...
Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全性的关键组件,它提供了一种框架,用于在Java应用程序中实现认证(Authentication)和授权(Authorization)。`jaas.jar` 文件是这个...
### JAAS:灵活的Java安全机制 #### 一、引言 Java Authentication and Authorization Service (JAAS, Java验证和授权API)为Java应用程序提供了一种扩展标准Java 2安全模型的方法。通过添加验证主题(即用户或其他...
通过理解JAAS的工作原理,以及如何配置和使用LoginModule,我们能够为用户提供安全的认证体验,并保护系统的资源不被未授权的访问。在"JaasExample"中,你可以深入学习这些概念,并实践身份验证的过程。
Java Authentication and Authorization Service (JAAS) 是 Java 平台提供的一个强大的验证和授权框架,它允许开发者实现安全策略,从而确保应用程序能够安全地运行。JAAS 提供了一种灵活的方式来管理用户的身份验证...
Java Authentication and Authorization Service (JAAS) 是 Java 平台中用于安全管理的重要组件,它提供了一种框架,使得应用程序可以进行用户身份验证和权限控制。在本文中,我们将深入探讨 JAAS 的核心概念、工作...
Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全性的框架,主要用于用户身份验证和权限授权。在Java应用程序中,尤其是服务器端应用,确保只有合法的用户能够访问资源是至关重要的。...
这个"Java JAAS安全认证 demo"是一个示例项目,旨在帮助开发者理解如何在Java应用中实施安全认证机制。JAAS是Java EE和Java SE应用程序进行安全控制的重要工具,它允许开发人员为不同的安全域定义灵活的身份验证策略...
Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全认证和授权的核心组件。它为开发者提供了一种标准的方式来管理用户的身份验证和访问控制,从而确保应用程序的安全性。在本精讲中,我们...
### 基于JAAS的Java安全编程 #### JAAS概览 JAAS(Java Authentication and Authorization Service,Java认证与授权服务)是Sun Microsystems为Java 2平台开发的一套安全框架扩展,旨在加强Java应用程序的安全性。...
Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全认证和授权的核心组件。这篇学习笔记将深入探讨JAAS的工作原理、配置以及在实际应用中的使用。通过阅读"JAAS学习笔记.doc"和"Weblogic ...
总结来说,扩展WebLogic的JAAS身份验证是通过编写自定义LoginModule并将其配置到WebLogic服务器的安全设置中来实现的。这个过程涉及到对JAAS框架的理解、Java编程以及WebLogic安全配置的掌握。通过这个过程,我们...
Java Authentication and Authorization Service (JAAS) 是Java平台的核心组件,用于提供安全的用户认证和权限管理。在Mac版Tomcat中配置JAAS,可以确保只有经过验证的用户才能访问Web应用程序,从而增强系统的安全...
Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全认证和授权的核心组件。它为开发者提供了一种标准的方式来实现用户身份验证和权限控制,从而增强了应用的安全性。"JAAS.rar_jaas"这个...