1

Я хотел бы создать аутентификацию на основе сертификата поверх связи с веб-рассылкой. Так что я создал WebSocket serverEndpoint и настроить SSL для аутентификации клиента с помощью причалу, как это:Извлечь клиентский сертификат 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) 

Есть ли способ как-то сохранить сертификат или информацию, содержащуюся и передать его к конечным точкам?

Спасибо за любую помощь :)

ответ

1

Я нашел решение, чтобы получить клиент, зарегистрированный в качестве UserPrincipal сессии, доступного по session.getUserPrincipal().

UserPricipal является «аутентифицированным пользователем для сеанса». Вы Nneed затем добавить службу Аутентификации к вашему 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) { 

} 

И вот это :)

+0

Привет, у вас есть образец рабочего проекта по этому вопросу? Я пытаюсь развернуть сервер websocket, и мне также нужно извлечь клиентский сертификат. Мне не повезло, что он работает так, как вы описали. – user1563721

Смежные вопросы