BSTEK Development Framework2(BDF2) : 3.3.替换用户

      BDF2中允许用户替换其中的用户、部门及岗位信息,而且这种替换可以根据需要只替换其中的用户信息或只替换其中的部门信息或只替换其中的岗位信息,或者全部替换掉。替换后的用户、部门及岗位信息可以来自用户定义的任何源,比如其它数据库、LDAP、WebService或其它系统。接下来我们就来介绍如何替换系统中采用的用户信息。

      为了演示替换操作,我们新建了一张用户表名为demo_user的表,它的结构比较简单,只有三个字段,结构图如下:

      用户表创建好之后,接下来我们就可以编写代码了,首先第一步,我们需要实现AbstractUser抽象类,定义我们自己的用户实体对象,其代码如下:

DemoUser类源码
import java.util.List;
import java.util.Map;
import com.bstek.bdf2.core.business.AbstractUser;
import com.bstek.bdf2.core.business.IDept;
import com.bstek.bdf2.core.business.IPosition;
import com.bstek.bdf2.core.model.Group;
import com.bstek.bdf2.core.model.Role;
public class DemoUser extends AbstractUser {
	private static final long serialVersionUID = 4348475318698431153L;
	private String username;
	private String cname;
	private String password;
	private List<IDept> depts;
	private List<IPosition> positions;
	private List<Role> roles;
	private List<Group> groups;
	public String getCname() {
		return cname;
	}
	public String getEname() {
		return cname;
	}
	/**
	 * 当前用户是否为系统管理员,如果这个属性返回true,那么就表示当前用户为系统管理员,
	 * 所有权限对其都不起作用,反之则不然。
	 * */
	public boolean isAdministrator() {
		return false;
	}
	/**
	 * 当前用户的手机号
	 * */
	public String getMobile() {
		return "13122112211";
	}
	/**
	 * 当前用户的email地址
	 * */
	public String getEmail() {
		return "test@sina.com";
	}
	/**
	 * 当前用户所在部门
	 * */
	public List<IDept> getDepts() {
		return depts;
	}
	/**
	 * 当前用户所拥有的岗位
	 * */
	public List<IPosition> getPositions() {
		return positions;
	}
	public void setPositions(List<IPosition> positions) {
		this.positions = positions;
	}
	public void setDepts(List<IDept> depts) {
		this.depts = depts;
	}
	/**
	 * 当前用户所拥有的角色,权限需要使用
	 * */
	public List<Role> getRoles() {
		return roles;
	}
	public void setRoles(List<Role> roles) {
		this.roles = roles;
	}
	/**
	 * 当前用户所在群组
	 * */
	public List<Group> getGroups() {
		return groups;
	}
	public void setGroups(List<Group> groups) {
		this.groups = groups;
	}
	/**
	 * 用户的其它元数据信息,如果我们的应用用不到,可直接返回null
	 * */
	public Map<String, Object> getMetadata() {
		return null;
	}
	public String getPassword() {
		return password;
	}
	public String getUsername() {
		return username;
	}
	/**
	 * 判断当前用户是否可用,可以在用户来源信息中判断这里取值,如果返回false,那么该用户将不能登录
	 */
	public boolean isEnabled() {
		return true;
	}
	/**
	 * 当前用户所在公司ID,如果当前系统只给一家公司使用,这里返回一个固定值即可,
	 * 如果是基于SAAS系统,那么这里需要根据公司信息返回一个值
	 * */
	public String getCompanyId() {
		return "bstek";
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public void setCname(String cname) {
		this.cname = cname;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

      在上面的代码当中,对于AbstractUser当中的一些重要方法我们做了描述,AbstractUser当中需要我们实现的方法都是必须的,而且都不能返回null(getMetadata方法除外),否则系统运行时可能会产生空指针的异常,开发当中,我们已尽量将AbstractUser类中需要实现的方法减到最少。

      编写好我们自定义的用户实体对象后,接下来我们需要实现IUserService接口,在这个接口当中定义了如何获取用户信息等相关操作,这个接口的源码如下:

IUserService接口源码
package com.bstek.bdf2.core.service;
import java.util.Collection;
import org.springframework.security.core.userdetails.UserDetailsService;
import com.bstek.bdf2.core.business.IUser;
import com.bstek.dorado.data.provider.Criteria;
import com.bstek.dorado.data.provider.Page;
/**
 * @since 2013-1-18
 * @author Jacky.gao
 */
public interface IUserService extends UserDetailsService {
	public static final String BEAN_ID="bdf2.userService";
	/**
	 * 分页加载用户数据
	 * @param page Dorado7分页对象,其中包含pageNo,pageSize,分页后的数据也填充到这个page对象当中,该参数不可为空
	 * @param companyId 要加载哪个companyId下的用户信息,该参数不可为空
	 * @param criteria Dorado7条件对象,可从中取到相应的条件值,该参数可为空
	 */
	void loadPageUsers(Page<IUser> page,String companyId,Criteria criteria);
	
	/**
	 * 加载指定部门ID下的用户信息
	 * @param deptId 隶属的部门ID,该参数不可为空
	 * @return 返回取到的用户集合
	 */
	Collection<IUser> loadUsersByDeptId(String deptId);
	
	/**
	 * 检查用户密码是否正确,如果不正确返回错误消息,如正确则返回null
	 * @param username 用户名
	 * @param password 要检查未加密的密码
	 * @return 不正确返回错误消息,如正确则返回null
	 */
	String checkPassword(String username,String password);
	
	/**
	 * 修改指定用户的密码信息
	 * @param username 用户名
	 * @param newPassword 新密码
	 */
	void changePassword(String username,String newPassword);
	
	/**
	 * 注册一个系统管理员账号
	 * @param username 用于登录的用户名
	 * @param cname 中文名
	 * @param ename 英文名
	 * @param password 密码
	 * @param mobile 手机号	
	 * @param email 电子邮件
	 * @param companyId 所在公司ID
	 */
	void registerAdministrator(String username,String cname,String ename,String password,String email,String mobile,String companyId);
	
	/**
	 * 根据用户名,实现化一个空的用户对象供系统使用,实例化的用户对象,只需要将给定的用户名填充进去即可
	 * @param username 用户名
	 * @return 实例化后的用户对象
	 */
	IUser newUserInstance(String username);
}

      接口中每个方法都有详细的解释,我们需要看理解它,接下来,我们来编写基于DemoUser的IUserService接口实现类,来从之前定义的demo_user表中获取用户信息,实现类代码如下:

IUserService接口实现类示例
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.bstek.bdf2.core.business.IUser;
import com.bstek.bdf2.core.orm.jdbc.JdbcDao;
import com.bstek.bdf2.core.service.IUserService;
import com.bstek.dorado.data.provider.Criteria;
import com.bstek.dorado.data.provider.Page;
public class DemoUserService extends JdbcDao implements IUserService {
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		String sql="select username,password,cname from demo_user where username=?";
		List<IUser> users=this.getJdbcTemplate().query(sql, new Object[]{username}, new UserMapper());
		if(users.size()==0){
			throw new UsernameNotFoundException("User ["+username+"]  is not exist!");
		}
		return users.get(0);
	}
	public void loadPageUsers(Page<IUser> page, String companyId) {
		String sql="select username,password,cname from demo_user where companyId=?";
		Collection<IUser> users=this.getJdbcTemplate().query(sql,new Object[]{companyId},new UserMapper());
		page.setEntities(users);
	}
	public Collection<IUser> loadUsersByDeptId(String deptId) {
		return Collections.EMPTY_LIST;
	}
	public String checkPassword(String username, String password) {
		return null;
	}
	public void changePassword(String username, String newPassword) {
	}
	public void registerAdministrator(String username, String cname,
			String ename, String password, String email, String mobile,
			String companyId) {
	}
	public IUser newUserInstance(String username) {
		DemoUser user=new DemoUser();
		user.setUsername(username);
		return user;
	}
	
	class UserMapper implements RowMapper<IUser>{
		public DemoUser mapRow(ResultSet rs, int rowNum) throws SQLException {
			DemoUser user=new DemoUser();
			user.setCname(rs.getString("cname"));
			user.setPassword(rs.getString("password"));
			user.setUsername(rs.getString("username"));
			return user;
		}
	}
}

      在这个接口实现当中,我们采用的是JdbcDao实现从demo_user表中获取用户信息,这其中我们保持checkPassword、changePassword与registerAdministrator三个方法为空,那么对应页面当中用户密码修改及注册系统管理员两块功能就不能正常使用,您可以去实现一把。

      再次,我们还需要实现IFrameworkService接口,这个接口比较简单,其中只有一个方法需要我们实现,这个方法的作用是处理用户登录时用户密码验证的,我们的实现类代码如下:

IFrameworkService实现类示例
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import com.bstek.bdf2.core.business.IUser;
import com.bstek.bdf2.core.service.IFrameworkService;
public class DemoFrameworkService implements IFrameworkService {
	public void authenticate(IUser user,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		DemoUser u=(DemoUser)user;
		String pwd=(String)authentication.getCredentials();
		if(!u.getPassword().equals(pwd)){
			 throw new BadCredentialsException("The password is invalid");
		}
	}
}

      可以看到,代码比较简单,就是判断用户密码是否正确,如果不正确抛出一个BadCridentialsException即可。

      上述三个接口实现完成之后,最后,我们需要将实现的DemoUserService及DemoFrameworkService配置到Spring当中,以替换系统自带的IUserService及IFrameworkService的默认实现类,首先我们来看如何配置DemoUserService实现类。

      打开位于WEB-INF/dorado-home目录下的datasources.xml文件,在其中添加如下配置:

DemoUserService配置示例
	<bean id="demoUserService" class="ext.DemoUserService"></bean>
	<bdf:user-service ref="demoUserService"/>

      第一行,我们将DemoUserService配置成一个bean,其id为demoUserService ,第二行我们利用BDF2提供的namespace scheam将系统默认的IUserService实现用demoUserService bean替换,当然如果您没有引入BDF2提供的namespace scheam,可以用下面的配置来代码上面两行配置信息:

DemoUserService另一种配置
<bean id="bdf2.userService" class="ext.DemoUserService"></bean>

      可以看到,这种配置更新简单,只需要一行就可以搞定,但前提是这个bean的ID必须是bdf2.userService。接下来我们看看如何配置DemoFrameworkService类,同样的方法,在datasources.xml当中添加下面两行配置:

DemoFrameworkService配置
	<bean id="demoFrameworkService" class="ext.DemoFrameworkService"></bean>
	<bdf:framework-service ref="demoFrameworkService"/>

      同样,如果您没有引入BDF2提供的namespace scheam,可以用下面的一行替代上述配置:

DemoFrameworkService另一种配置
<bean id="bdf2.frameworkService" class="ext.DemoFrameworkService"></bean>

      需要注意的是其ID必须为bdf2.frameworkService。

      上述操作完成之后,就可以在demo_user中插入一个用户,启动我们应用进行登录了。

Attachments:

demo-user.png (image/png)