面试题答案
一键面试JAAS基本架构
- Subject(主体):代表一个特定的实体(通常是用户),它包含了该实体的相关信息,如身份(Principal)和安全属性(Credential)。
- Principal(身份):表示Subject的身份信息,例如用户名、角色等。
- LoginModule(登录模块):负责实际的认证工作,如验证用户名和密码、处理证书等。多个LoginModule可以组合使用以实现复杂的认证场景。
- Configuration(配置):定义了如何使用LoginModule进行认证,通过配置文件来指定使用哪些LoginModule以及它们的顺序和参数。
- Policy(策略):决定Subject是否被授权执行特定的操作,授权基于Subject的Principal和相关权限。
实现自定义认证模块思路
- 创建一个实现
LoginModule
接口的类。 - 在该类中实现接口定义的方法,包括初始化、登录、提交、注销等逻辑。
- 在登录方法中,从自定义数据源获取用户信息,并与用户输入的用户名和密码进行匹配。
- 通过配置文件将自定义的
LoginModule
集成到JAAS框架中。
关键代码片段
import javax.security.auth.Subject;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import javax.security.auth.spi.LoginModule;
import java.util.Map;
public class CustomLoginModule implements LoginModule {
private Subject subject;
private CallbackHandler callbackHandler;
private String username;
private String password;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String,?> sharedState, Map<String,?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
}
@Override
public boolean login() throws LoginException {
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("Username: ");
callbacks[1] = new PasswordCallback("Password: ", false);
try {
callbackHandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
password = new String(((PasswordCallback) callbacks[1]).getPassword());
} catch (IOException | UnsupportedCallbackException e) {
throw new LoginException("Failed to get username or password");
}
// 从自定义数据源验证用户名和密码
if (authenticateUser(username, password)) {
return true;
} else {
throw new LoginException("Invalid username or password");
}
}
private boolean authenticateUser(String username, String password) {
// 自定义数据源验证逻辑,例如从数据库或文件中读取用户信息
// 这里简单示例,实际应用需要替换为真实逻辑
if ("testUser".equals(username) && "testPassword".equals(password)) {
return true;
}
return false;
}
@Override
public boolean commit() throws LoginException {
return true;
}
@Override
public boolean abort() throws LoginException {
return true;
}
@Override
public boolean logout() throws LoginException {
return true;
}
}
配置文件示例(jaas.config)
CustomLogin {
com.example.CustomLoginModule required;
};
使用示例
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.security.Principal;
import java.util.Set;
public class Main {
public static void main(String[] args) {
try {
LoginContext loginContext = new LoginContext("CustomLogin", new Subject());
loginContext.login();
Subject subject = loginContext.getSubject();
Set<Principal> principals = subject.getPrincipals();
System.out.println("Authenticated user: " + principals);
loginContext.logout();
} catch (LoginException e) {
e.printStackTrace();
}
}
}
在运行上述代码前,需要通过System.setProperty("java.security.auth.login.config", "jaas.config");
指定配置文件路径。