2015-01-20 21 views
6

Tôi đang sử dụng Spring SAML trong ứng dụng nhiều người thuê để cung cấp SSO. Các đối tượng thuê khác nhau sử dụng các url khác nhau để truy cập ứng dụng và mỗi url có một Trình cung cấp danh tính riêng được định cấu hình. Làm cách nào để tự động gán Nhà cung cấp nhận dạng chính xác cho url được sử dụng để truy cập ứng dụng?Làm cách nào để tự động chọn nhà cung cấp nhận dạng SAML được định cấu hình trong môi trường nhiều người thuê để thực hiện SSO bằng cách sử dụng Spring SAML

Ví dụ:

thuê 1: http://tenant1.myapp.com

thuê 2: http://tenant2.myapp.com

tôi thấy rằng tôi có thể thêm một IDP tham số url (http://tenant1.myapp.com?idp=my.idp.entityid.com) và SAMLContextProvider sẽ chọn nhà cung cấp danh tính với id thực thể đó. Tôi đã phát triển một MetadataProvider được cơ sở dữ liệu hỗ trợ, lấy tên máy chủ cho thuê như tham số khởi tạo để tìm nạp siêu dữ liệu cho biểu mẫu thuê của cơ sở dữ liệu được liên kết với tên máy chủ đó. Bây giờ tôi nghĩ rằng tôi cần một số cách để lặp qua các nhà cung cấp siêu dữ liệu để liên kết entityId của siêu dữ liệu với tên máy chủ. Tuy nhiên, tôi không thấy cách tôi có thể tìm nạp entityId của siêu dữ liệu. Điều đó sẽ giải quyết vấn đề của tôi.

Trả lời

6

Bạn có thể xem cách phân tích cú pháp thực thể có sẵn trong số MetadataProvider theo phương pháp MetadataManager#parseProvider. Lưu ý rằng thông thường, mỗi nhà cung cấp có thể cung cấp nhiều định nghĩa IDP và SP, không chỉ một định nghĩa. Ngoài ra, bạn có thể mở rộng thêm ExtendedMetadataDelegate với lớp của riêng mình, bao gồm bất kỳ siêu dữ liệu bổ sung nào (như entityId) mà bạn muốn và sau đó chỉ cần nhập lại MetadataProvider vào lớp tùy chỉnh của bạn và nhận thông tin từ đó khi lặp dữ liệu qua MetadataManager.

Nếu tôi là bạn, tôi sẽ có một chút cách tiếp cận khác nhau. Tôi sẽ mở rộng SAMLContextProviderImpl, phương pháp ghi đè populatePeerEntityId và thực hiện tất cả khớp với tên máy chủ/IDP tại đó. Xem chi tiết original method.

+3

Tôi đã tạo SAMLContextProvider của riêng mình và bỏ qua populatePeerIdentityId. Điều đó làm việc tuyệt vời. Một khi tôi đã được thực hiện tôi nhận ra rằng SAMLContextProvider chỉ được sử dụng trong SP khởi xướng SP. Chúng tôi chủ yếu sử dụng SSO do IDP khởi xướng, vì vậy tôi cũng cần phải bao gồm cả SSO.Tôi đã kết thúc kiểm tra peerEntityID của tin nhắn gửi đến đối với IDP entityID được cấu hình cho đối tượng thuê đó trong SAMLAuthenticationProvider tùy chỉnh của tôi. – MarcFasel

+1

Tính năng này của nhà cung cấp nhận dạng bản đồ đối với nhà cung cấp dịch vụ là chìa khóa để hỗ trợ cho nhiều người thuê. Đây có phải là kế hoạch phát hành sắp tới không? – MarcFasel

+0

Chúng ta sẽ thấy, dự án phụ thuộc vào thời gian rảnh rỗi của tôi (nó không được tài trợ bởi bất kỳ ai) và không có nhiều. Cải thiện đa hợp đồng thuê nhà là điều tôi muốn hoàn thành. –

3

Tại thời điểm viết bài, Spring SAML có phiên bản 1.0.1.FINAL. Nó không hỗ trợ cho việc thuê nhiều bên ngoài hộp. Tôi đã tìm ra một cách khác để đạt được sự thuê nhiều bên ngoài những gợi ý của Vladimir ở trên. Nó rất đơn giản và thẳng thắn và không yêu cầu mở rộng của bất kỳ lớp học Spring SAML nào. Hơn nữa, nó sử dụng xử lý các bí danh trong xây dựng của Spring SAML trong CachingMetadataManager.

Trong bộ điều khiển, hãy nắm bắt tên người thuê nhà từ yêu cầu và tạo đối tượng ExtendedMetadata bằng cách sử dụng tên người thuê làm bí danh. Tiếp theo, tạo một số ExtendedMetadataDelegate trong số ExtendedMetadata và khởi tạo. Phân tích cú pháp các id thực thể ra khỏi nó và kiểm tra xem chúng có tồn tại trong MetadataManager hay không. Nếu chúng không tồn tại, hãy thêm nhà cung cấp và làm mới siêu dữ liệu. Sau đó lấy id tổ chức từ MetadataManager bằng cách sử dụng getEntityIdForAlias().

Đây là mã cho bộ điều khiển. Có ý kiến ​​nội tuyến giải thích một số thông báo:

@Controller 
public class SAMLController { 

    @Autowired 
    MetadataManager metadataManager; 

    @Autowired 
    ParserPool parserPool; 

    @RequestMapping(value = "/login.do", method = RequestMethod.GET) 
    public ModelAndView login(HttpServletRequest request, HttpServletResponse response, @RequestParam String tenantName) 
                 throws MetadataProviderException, ServletException, IOException{ 
     //load metadata url using tenant name 
     String tenantMetadataURL = loadTenantMetadataURL(tenantName); 

     //Deprecated constructor, needs to change 
     HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(tenantMetadataURL, 15000); 
     httpMetadataProvider.setParserPool(parserPool); 

     //Create extended metadata using tenant name as the alias 
     ExtendedMetadata metadata = new ExtendedMetadata(); 
     metadata.setLocal(true); 
     metadata.setAlias(tenantName); 

     //Create metadata provider and initialize it 
     ExtendedMetadataDelegate metadataDelegate = new ExtendedMetadataDelegate(httpMetadataProvider, metadata); 
     metadataDelegate.initialize(); 

     //getEntityIdForAlias() in MetadataManager must only be called after the metadata provider 
     //is added and the metadata is refreshed. Otherwise, the alias will be mapped to a null 
     //value. The following code is a roundabout way to figure out whether the provider has already 
     //been added or not. 

     //The method parseProvider() has protected scope in MetadataManager so it was copied here   
     Set<String> newEntityIds = parseProvider(metadataDelegate); 
     Set<String> existingEntityIds = metadataManager.getIDPEntityNames(); 

     //If one or more IDP entity ids do not exist in metadata manager, assume it's a new provider. 
     //If we always add a provider without this check, the initialize methods in refreshMetadata() 
     //ignore the provider in case of a duplicate but the duplicate still gets added to the list 
     //of providers because of the call to the superclass method addMetadataProvider(). Might be a bug. 
     if(!existingEntityIds.containsAll(newEntityIds)) { 
      metadataManager.addMetadataProvider(metadataDelegate); 
      metadataManager.refreshMetadata(); 
     } 

     String entityId = metadataManager.getEntityIdForAlias(tenantName); 

     return new ModelAndView("redirect:/saml/login?idp=" + URLEncoder.encode(entityId, "UTF-8")); 
    } 

    private Set<String> parseProvider(MetadataProvider provider) throws MetadataProviderException { 
     Set<String> result = new HashSet<String>(); 

     XMLObject object = provider.getMetadata(); 
     if (object instanceof EntityDescriptor) { 
      addDescriptor(result, (EntityDescriptor) object); 
     } else if (object instanceof EntitiesDescriptor) { 
      addDescriptors(result, (EntitiesDescriptor) object); 
     } 

     return result; 

    } 

    private void addDescriptors(Set<String> result, EntitiesDescriptor descriptors) throws MetadataProviderException { 
     if (descriptors.getEntitiesDescriptors() != null) { 
      for (EntitiesDescriptor descriptor : descriptors.getEntitiesDescriptors()) { 
       addDescriptors(result, descriptor); 
      } 
     } 

     if (descriptors.getEntityDescriptors() != null) { 
      for (EntityDescriptor descriptor : descriptors.getEntityDescriptors()) { 
       addDescriptor(result, descriptor); 
      } 
     } 
    } 

    private void addDescriptor(Set<String> result, EntityDescriptor descriptor) throws MetadataProviderException { 
     String entityID = descriptor.getEntityID(); 
     result.add(entityID); 
    } 
} 

Tôi tin rằng điều này trực tiếp giải quyết vấn đề của OP về cách tìm IDP cho người thuê đã cho. Nhưng điều này sẽ chỉ làm việc cho IDP với một id thực thể duy nhất.

+0

Rất hữu ích! Cảm ơn bài viết đầu tiên của bạn! – Zarial

+0

Chỉ muốn chỉ ra rằng giải pháp này không hoạt động trong môi trường nhóm trừ khi bạn có phiên cố định cho người dùng của mình .... Yêu cầu ban đầu tới /login.do thêm nhà cung cấp siêu dữ liệu vào JVM được liên kết với yêu cầu đó, tuy nhiên người dùng có thể quay lại ứng dụng trên một JVM khác mà không biết IDP đã bắt đầu quá trình xác thực ... – danw

Các vấn đề liên quan