我找到了一个解决方案,让客户端注册为会话的 UserPrincipal,session.getUserPrincipal() 可以访问。
UserPricipal 是“会话的经过身份验证的用户”。然后,您需要向 ServletContextHandler 添加身份验证服务,如下所示:
//Create SSL ContextFactory with appropriate attributes
...
//Create the connector
...
//Create ContextHandler
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/example");
//Add security contraint to the context => authentication
ConstraintSecurityHandler security = new ConstraintSecurityHandler();
Constraint constraint = new Constraint();
constraint.setName("auth");
constraint.setAuthenticate(true);
constraint.setRoles(new String[]{"user"});
Set<String> knownRoles = new HashSet<String>();
knownRoles.add("user");
ConstraintMapping mapping = new ConstraintMapping();
mapping.setPathSpec("/*");
mapping.setConstraint(constraint);
security.setConstraintMappings(Collections.singletonList(mapping), knownRoles);
security.setAuthMethod("CLIENT-CERT");
LoginService loginService = new HashLoginService();
security.setLoginService(loginService);
security.setAuthenticator(new ClientCertAuthenticator());
context.setSecurityHandler(security);
这样,当客户端连接到 websocket 端点时,安全处理程序确保客户端必须经过身份验证。据我了解,ClientCertAuthenticator 将检查客户端请求以提取信息(证书的 DN),然后将其传递给 LoginService,在该处对客户端进行身份验证以及会话集的 UserPricipal。
这里的问题是你必须有一个有效的登录服务(例如,HashLoginService 是一个使用密码和用户名的内存登录服务,JDBCLoginService 使用数据库)。对于像我一样只想从证书中提取所需信息并随后使用此信息执行身份验证的人,您可以提供自己的 LoginService 接口实现。
这是我所做的:
在定义您的安全处理程序期间:
LoginService loginService = new CustomLoginService();
loginService.setIdentityService(new DefaultIdentityService());
security.setLoginService(loginService);
CustomLoginService 类
public class CustomLoginService implements LoginService {
IdentityService identityService = null;
@Override
public String getName() {
return "";
}
@Override
public UserIdentity login(String username, Object credentials) {
//you need to return a UserIdentity, which takes as argument:
// 1. A Subjet, containing a set of principals, a set of private credentials and a set of public ones (type Object)
// 2. A Principal of this Subject
// 3. A set of roles (String)
LdapPrincipal principal = null;
try {
principal = new LdapPrincipal(username);
//you need to have a Principal. I chose LDAP because it is specifically intended for user identified with a DN.
} catch (InvalidNameException e) {
e.printStackTrace();
}
String[] roles = new String[]{"user"};
return new DefaultUserIdentity(
new Subject(false,
new HashSet<LdapPrincipal>(Arrays.asList(new LdapPrincipal[]{principal}) ),
new HashSet<Object>(Arrays.asList(new Object[]{credentials})),
new HashSet<Object>(Arrays.asList(new Object[]{credentials}))),
principal,
roles);
}
@Override
public boolean validate(UserIdentity user) {
return false;
}
@Override
public IdentityService getIdentityService() {
return identityService;
}
@Override
public void setIdentityService(IdentityService service) {
identityService = service;
}
@Override
public void logout(UserIdentity user) {
}
就是这样:)