【IT168 技术文档】JAAS:Java Authentication and Authorization Service,提供了认证和授权框架。
本例是认证的实现,JAAS定义了可插拔的认证机制,使认证逻辑独立开来,可通过修改配置文件切换认证模块。
官方参考:
http://java.sun.com/products/archive/jaas/
http://java.sun.com/j2se/1.4.2/docs/guide/security/jaas/JAASRefGuide.html
security.pdf
一、配置文件及设置
1. 配置文件(假设为D:/jaas.conf):
Sample{ com.fastunit.samples.jaas.SampleLoginModule required debug=false; };
此文件定义了一个“Sample”验证模块,使用SampleLoginModule来进行验证。
2. 启用配置文件:
-Djava.security.auth.login.config=D:/jaas.conf
二、客户端调用
import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import javax.servlet.http.HttpServletRequest; public class LoginManager { public static boolean login(HttpServletRequest request) { try { String username = request.getParameter("username"); String password = request.getParameter("password"); //此处指定了使用配置文件的“Sample”验证模块,对应的实现类为SampleLoginModule LoginContext lc = new LoginContext("Sample", new SampleCallbackHandler( username, password)); lc.login();// 如果验证失败会抛出异常 return true; } catch (LoginException e) { e.printStackTrace(); return false; } catch (SecurityException e) { e.printStackTrace(); return false; } } } import java.io.IOException; 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 SampleCallbackHandler implements CallbackHandler { private String username; private String password; public SampleCallbackHandler(final String username, final String password) { this.username = username; this.password = password; } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int index = 0; index < callbacks.length; index++) { if (callbacks[index] instanceof NameCallback) { NameCallback ncb = (NameCallback) callbacks[index]; ncb.setName(username); } if (callbacks[index] instanceof PasswordCallback) { PasswordCallback pcb = (PasswordCallback) callbacks[index]; pcb.setPassword(password.toCharArray()); } } } }
三、验证实现
import java.io.IOException; 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.LoginException; import javax.security.auth.spi.LoginModule; public class SampleLoginModule implements LoginModule { private boolean isAuthenticated = false; private CallbackHandler callbackHandler; private Subject subject; private SamplePrincipal principal; public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { this.subject = subject; this.callbackHandler = callbackHandler; } public boolean login() throws LoginException { try { NameCallback nameCallback = new NameCallback("username"); PasswordCallback passwordCallback = new PasswordCallback("password", false); final Callback[] calls = new Callback[] { nameCallback, passwordCallback }; // 获取用户数据 callbackHandler.handle(calls); String username = nameCallback.getName(); String password = String.valueOf(passwordCallback.getPassword()); // TODO 验证,如:查询数据库、LDAP。。。 if (true) {// 验证通过 principal = new SamplePrincipal(username); isAuthenticated = true; } else { throw new LoginException("user or password is wrong"); } } catch (IOException e) { throw new LoginException("no such user"); } catch (UnsupportedCallbackException e) { throw new LoginException("login failure"); } return isAuthenticated; } /** * 验证后的处理,在Subject中加入用户对象 */ public boolean commit() throws LoginException { if (isAuthenticated) { subject.getPrincipals().add(principal); } else { throw new LoginException("Authentication failure"); } return isAuthenticated; } public boolean abort() throws LoginException { return false; } public boolean logout() throws LoginException { subject.getPrincipals().remove(principal); principal = null; return true; } } import java.security.Principal; public final class SamplePrincipal implements Principal { private String name; public SamplePrincipal(String name) { this.name = name; } public String getName() { return name; } public boolean equals(Object o) { return (o instanceof SamplePrincipal) && this.name.equalsIgnoreCase(((SamplePrincipal) o).name); } public int hashCode() { return name.toUpperCase().hashCode(); } } org.hibernate.dialect.MySQL5Dialect true org.hibernate.cache.EhCacheProvider true
再然后,还是修改这个文件,定义多个不同的事务管理器,它们之间只有sessionFactory属性不同,如下:
以上的这些修改是最基本的,就是把sessionFactory和事务管理器都定义为多份的,以后,在要使用到这些东西的时候,就不能使用Spring提供的自动按照name匹配的方便了,必须得自己指定。
利用SpringSide2.0,只需要编写表示Model的POJO,就可以很容易得到数据访问层的代码,比如,我的网站有一个表示文章分类的数据表,其对应的Model如下:
package com.yumdays.model; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name="catalogs") public class Catalog implements Serializable { private String id; private String name; @Id @GenericGenerator(name="system-uuid",strategy="uuid") public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
那么,其DAO层的代码如下:
package com.yumdays.service; import org.springside.core.dao.HibernateEntityDao; import com.yumdays.model.Catalog; public class CatalogManager extends HibernateEntityDao { }
相当的简洁,该CatalogManager类提供了非常完整的find、get、save等功能。下一步,将这个类是用Spring管理起来的时候,就必须自己指定sessionFactory了,因此,在src/resources/spring/目录下的serviceContext.xml文件中,配置的形式如下:
至于事务管理器,是在配置事务的时候用到的,因此,修改src/resources/spring目录下的applicationContext.xml文件,如下:
OK,快速启动项目,但是还是没有成功,报出的错误提示说没有定义name为transactionManager的bean。经过我这么一改,当然没有定义这个bean了,但是我也没有用它啊?我用的是transactionManagerIndex,经过多次排查,我仍然没有找出问题的症结所在。
由此可见,使用默认值的地方太多了,也是会害死人的。SpringSide一定要吸取这个教训。