技术开发 频道

基于JAAS实现登录

  【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一定要吸取这个教训。

0
相关文章