package smartbi.usermanager.auth.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.servlet.ServletContext;
import org.apache.log4j.Logger;
import smartbi.usermanager.auth.*;
import smartbi.util.StringUtil;
/**
* 此类为使用Microsoft的AD(活动目录)登录验证的一个例子。<br>
* 需要配置文件ad.properties,并放入到war包中的WEB-INF目录中,配置文件中包含的键值为:<br>
*
* <pre>
* # 上下文对象工厂类,一般不需要改变
* initial_context_factory=com.sun.jndi.ldap.LdapCtxFactory
* # 活动目录的访问地址
* provider_url=ldap://ADServer:3268
* # 活动目录的登录用户名前缀
* principal_prefix=MyDomain\\
* # 活动目录的登录用户名
* login_user=username
* # 活动目录的登录密码
* login_password=password
* </pre>
*/
public class ADAuthentication implements IAuthentication {
private static final Logger log = Logger.getLogger(ADAuthentication.class);
private String initialContextFactory;
private String providerUrl;
private String prefix;
private String loginUser;
private String loginPassword;
private String baseName;
private String filterPrefix;
private String filterSuffix;
/**
* 配置登录验证类
*
* @param ctx
* 应用的上下文对象
* @throws IOException
* 当出现初始化错误时。
*/
public void config(ServletContext ctx) throws IOException {
init(ctx.getResourceAsStream("/WEB-INF/ad.properties"));
}
/**
* 读取必要的配置信息
*
* @param is
* 配置文件
* @throws IOException
* 当读取配置文件出错时抛出
*/
private void init(InputStream is) throws IOException {
if (is != null) {
Properties prop = new Properties();
prop.load(is);
initialContextFactory = prop.getProperty("initial_context_factory");
providerUrl = prop.getProperty("provider_url");
prefix = prop.getProperty("principal_prefix");
loginUser = prop.getProperty("login_user");
loginPassword = prop.getProperty("login_password");
baseName = StringUtil.nullToEmpty(prop.getProperty("baseName"));
filterPrefix = StringUtil.nullToEmpty(prop.getProperty("filterPrefix"));
filterSuffix = StringUtil.nullToEmpty(prop.getProperty("filterSuffix"));
is.close();
}
}
/**
* 判断登录用户名、密码是否正确
*
* @param userName
* 用户名
* @param password
* 密码
* @return 如果登录成功则返回true,如果失败则返回false
* @throws IOException
* 当出现通信异常
*/
public boolean isPasswordValidate(String userName, String password) throws IOException {
if (StringUtil.isNullOrEmpty(password)) {
return false;
}
try {
Hashtable<String, String> env = initEnv(userName, password);
LdapContext context = new InitialLdapContext(env, null);
context.close();
return true;
} catch (NamingException e) {
return false;
}
}
/**
* 初始化访问活动目录所需要的参数设置
*
* @param userName
* 登录活动目录的用户名
* @param password
* 登录活动目录的密码
* @return 返回相应的参数配置
*/
private Hashtable<String, String> initEnv(String userName, String password) {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
env.put(Context.PROVIDER_URL, providerUrl);
env.put(Context.SECURITY_PRINCIPAL, prefix + userName);
env.put(Context.SECURITY_CREDENTIALS, password);
return env;
}
/**
* 获取用户的名称、别名信息
*
* @param userName
* 用户名
* @return 指定用户的信息
* @throws IOException
* 当出现通信异常
* @throws UserNotExistException
* 如果抛出此异常,则smartbi会删除此用户
*/
public UserInfo getUser(String userName) throws IOException, UserNotExistException {
try {
// AD-使用配置文件中的用户名、密码登录
Hashtable<String, String> env = initEnv(loginUser, loginPassword);
LdapContext context = new InitialLdapContext(env, null);
// baseName 可以通过Softerra LDAP Browser查看
String base = baseName;
// AD-指定搜索范围为个人,并且用户名为客户端输入的用户名称
String filter = filterPrefix + userName + filterSuffix;
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// AD-返回用户的名称、别名及部门
controls.setReturningAttributes(new String[] { "sAMAccountName", "displayName", "department" });
NamingEnumeration<SearchResult> answer = context.search(base, filter, controls);
if (answer.hasMore()) {
// 如果找到相应的用户
SearchResult result = answer.next();
Attributes attrs = result.getAttributes();
Attribute attr = attrs.get("sAMAccountName");
String name = attr == null || attr.get() == null ? null : attr.get().toString();
attr = attrs.get("displayName");
String alias = attr == null || attr.get() == null ? null : attr.get().toString();
attr = attrs.get("department");
String dept = attr == null || attr.get() == null ? null : attr.get().toString();
answer.close();
// 设置用户信息,smartbi会自动同步
UserInfo info = new UserInfo(name, alias, alias);
// 设置组信息
GroupInfo group = new GroupInfo();
group.setName(dept);
// 设置组的父组为null,即此组会建立在smartbi的"默认组"之下
// 如果不为null,smartbi会根据此属性来建立用户组的树结构
group.setParentGroup(null);
// smartbi支持一个用户同时属于多个用户组,所以使用List<GroupInfo>
List<GroupInfo> groups = new ArrayList<GroupInfo>();
groups.add(group);
// 设置用户所属的组
info.setGroups(groups);
// AD不支持返回角色列表,所以设置角色为null,smartbi不会修改此用户所属的角色
info.setRoles(null);
return info;
}
// 如果用户不存在,则抛出异常
throw new UserNotExistException(userName);
} catch (NamingException e) {
log.error("getUser fail.", e);
return null;
}
}
/**
* 返回用户是否在本Authentication类中验证密码
*
* @param userName
* 用户名
* @return 如果返回true,表示该用户的密码在本类中验证;如果返回false,表示该用户的密码在知识库中验证
* @throws IOException
* 当出现通信异常
*/
public boolean shallUserValidateInAuthentication(String userName) throws IOException {
// admin用户还是使用知识库本身的方式验证
if ("admin".equals(userName))
return false;
else
return true;
}
/**
* 设置用户的密码
*
* @param oldPassword
* 旧密码
* @param newPassword
* 新密码
* @return 返回设置用户密码是否成功
* @throws IOException
* 当出现通信异常时抛出
* @throws UnsupportedOperationException
* 当该用户验证类不支持此操作时抛出
*/
public boolean changePassword(String userName, String oldPassword, String newPassword) throws IOException,
UnsupportedOperationException {
throw new UnsupportedOperationException();
}
} |