`
grandboy
  • 浏览: 125998 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

安全:JAAS LoginModule(转载)

    博客分类:
  • JAAS
阅读更多

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提供的公开的安全模块和处理许可证的机制。

分享到:
评论

相关推荐

    wildfly-custom-login-module:自定义LoginModule for Wildfly

    通常,Java提供了一些预定义的LoginModule,如`jaas.JDBCLoginModule`或`jaas.FileLoginModule`,但这些可能无法满足所有业务场景。因此,创建自定义LoginModule对于扩展和适应特定的认证和授权逻辑至关重要。 ...

    JAAS Demo

    4. **验证身份**:LoginModule负责验证提供的凭据是否有效,成功后将对应的Principal添加到Subject中。 5. **授权**:一旦认证成功,LoginContext将调用commit()方法,将认证后的Subject设置为当前线程的安全上下文...

    JAAS登录验证文档,里面有详细步骤及例子

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全认证和授权的核心组件。它提供了一个框架,使得应用程序可以通过统一的方式来处理用户身份验证和权限控制。JAAS的主要目标是简化应用的...

    Jaas in Action (java 安全)

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全认证和授权的核心组件。它为开发者提供了一种标准的方式来实现用户身份验证和访问控制,从而确保应用程序的安全性。"Jaas in Action"这...

    JAAS 身份验证技术入门

    4. **身份验证**:LoginModule完成身份验证后,会创建一个或多个Principal对象,并将其添加到Subject中,表示认证成功。 5. **授权**:一旦Subject通过身份验证,就可以基于Subject的权限执行授权操作,决定用户...

    shibboleth_django-freeradius_login:Jaas登录模块将Shibboleth IDP连接到Django

    该项目实现了Java的JAAS LoginModule,它允许Shibboleth idp服务器使用模块进行身份验证。 要求 该项目与Shibboleth idp服务器和django-freeradius模块一起使用,因此请参见以下文档链接: ; 。 此外,该模块...

    JAAS简介及示例代码

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全性的核心组件,它为开发者提供了一种机制来处理用户身份验证和权限控制。这个服务允许应用程序执行基于角色的安全性,这意味着用户的...

    java软件包文件 jaas.jar

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全性的关键组件,它提供了一种框架,用于在Java应用程序中实现认证(Authentication)和授权(Authorization)。`jaas.jar` 文件是这个...

    JAAS:灵活的Java安全机制

    ### JAAS:灵活的Java安全机制 #### 一、引言 Java Authentication and Authorization Service (JAAS, Java验证和授权API)为Java应用程序提供了一种扩展标准Java 2安全模型的方法。通过添加验证主题(即用户或其他...

    Jaas-in-action-book

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于实现安全认证和授权的核心框架。它为开发者提供了一种标准化的方法来处理用户身份验证和权限控制,使得应用程序能够根据用户的身份和角色来...

    JAAS简介及实例

    通过理解JAAS的工作原理,以及如何配置和使用LoginModule,我们能够为用户提供安全的认证体验,并保护系统的资源不被未授权的访问。在"JaasExample"中,你可以深入学习这些概念,并实践身份验证的过程。

    JAAS灵活的Java安全机制

    JAAS是Java安全模型的重要组成部分,它允许开发者构建安全的应用程序,满足不同级别的安全需求。 在JAAS中,认证(Authentication)是指验证用户身份的过程,而授权(Authorization)则是确定已认证的用户是否有权...

    jaas资料 基于JAAS和J2EE Web容器的验证与授权

    在 JAAS 中,这个过程通常涉及两个关键组件:LoginModule 和 CallbackHandler。LoginModule 负责具体的验证逻辑,而 CallbackHandler 则用于收集必要的用户输入(例如用户名和密码)。 ##### 2. 授权...

    java JAAS登陆验证

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全性的框架,主要用于用户身份验证和权限授权。在Java应用程序中,尤其是服务器端应用,确保只有合法的用户能够访问资源是至关重要的。...

    jaas规范实现代码

    Java Authentication and Authorization Service (JAAS) 是 Java 平台中用于安全管理的重要组件,它提供了一种框架,使得应用程序可以进行用户身份验证和权限控制。在本文中,我们将深入探讨 JAAS 的核心概念、工作...

    jaas in action

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全性的核心组件,它提供了一种框架,用于在Java应用程序中实现用户身份验证和权限管理。《JAAS in Action》是一本深入探讨这一主题的英文...

    Java JAAS安全认证 demo

    这个"Java JAAS安全认证 demo"是一个示例项目,旨在帮助开发者理解如何在Java应用中实施安全认证机制。JAAS是Java EE和Java SE应用程序进行安全控制的重要工具,它允许开发人员为不同的安全域定义灵活的身份验证策略...

    基于JAAS的Java安全编程

    ### 基于JAAS的Java安全编程 #### JAAS概览 JAAS(Java Authentication and Authorization Service,Java认证与授权服务)是Sun Microsystems为Java 2平台开发的一套安全框架扩展,旨在加强Java应用程序的安全性。...

    jaas详细配置精讲

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全认证和授权的核心组件。它为开发者提供了一种标准的方式来管理用户的身份验证和访问控制,从而确保应用程序的安全性。在本精讲中,我们...

Global site tag (gtag.js) - Google Analytics