2015-05-14 128 views
1

我想在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) 

有莫名其妙的方式存儲證書或包含的信息和將它傳遞給端點?

感謝所有幫助:)

回答

1

我找到了一個解決方案通過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) { 

} 

就是這樣:)

+0

嘿,你有工作項目樣本嗎?我正在嘗試部署websocket服務器,並且還需要提取客戶端證書。我沒有運氣讓你像你所描述的那樣工作。 – user1563721

相關問題