从安全的websocket连接中提取客户端X509证书
我想在websocket通信之上创建基于证书的身份验证。 所以我创建了一个WebSocket的serverEndpoint,并成立了客户端身份验证SSL与码头的帮助下,像这样:从安全的websocket连接中提取客户端X509证书
Server server = new Server();
//Create SSL ContextFactory with appropriate attributes
SslContextFactory sslContextFactory = new SslContextFactory();
//Set up keystore path, truststore path, passwords, etc
...
sslContextFactory.setNeedClientAuth(true);
//Create the connector
ServerConnector localhostConnector = new ServerConnector(server, sslContextFactory);
localhostConnector.setHost(...);
localhostConnector.setPort(...);
server.addConnector(localhostConnector);
//Create ContextHandler
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/example");
server.setHandler(context);
// Initialize the JSR-356 layer and add custom Endpoints
ServerContainer container = WebSocketServerContainerInitializer.configureContext(context);
container.addEndpoint(Endpoint1.class); //Annotated class
container.addEndpoint(Endpoint2.class);
SSL配置似乎是正确的,因为我可以用SSL连接到不同的端点我写的客户端(错误的证书导致连接终止)。
现在,我想提取客户端证书中包含的信息。我看到了,我可以从一个的SSLSession拿到证书,但我在端点要访问的唯一会话是一个“正常”的会议:
@OnOpen
@Override
public void open(final Session session, final EndpointConfig config)
有莫名其妙的方式存储证书或包含的信息和将它传递给端点?
感谢所有帮助:)
我找到了一个解决方案通过session.getUserPrincipal()
获得注册为会议的UserPrincipal客户端访问。
UserPricipal是“会话的认证用户”。您nneed然后到authentiation服务添加到您的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设置。
这里的问题是,你必须有一个工作的loginService(例如,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) {
}
就是这样:)
嘿,你有工作项目样本吗?我正在尝试部署websocket服务器,并且还需要提取客户端证书。我没有运气让你像你所描述的那样工作。 – user1563721