2015-08-28 106 views
1

我目前正在嘗試實現Spring Boot webservice和相互身份驗證,這些身份驗證需要用戶驗證並通過它對ldap服務器包含的詳細信息對用戶進行身份驗證和授權。Spring Boot/Spring Secruity針對LDAP的證書身份驗證

迄今爲止相互認證工作,服務器向用戶標識自己並要求提供用戶證書。以內存用戶爲例,整個身份驗證和授權過程都可以正常工作。然而,一旦我實現LDAP連接,我得到一個「java.lang.IllegalStateException:UserDetailsS​​ervice是必需的。」例外。有趣的是,當我使用登錄頁面時,LDAP配置本身工作正常,用戶不得不手動提示他的證書。因此,在短期:

登錄頁面+ LDAP作品,

CERT +內存用戶工作,

CERT + LDAP不起作用。

這是我到目前爲止的代碼:

網/配置/ Application.java

@SpringBootApplication 
    @ComponentScan({ "web.*" }) 
    public class Application extends SpringBootServletInitializer { 

     @Bean 
     public InternalResourceViewResolver viewResolver() { 
      InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); 
      viewResolver.setViewClass(JstlView.class); 
      viewResolver.setPrefix("/WEB-INF/jsp/"); 
      viewResolver.setSuffix(".jsp"); 
      return viewResolver; 
     } 

     public static void main(String[] args) throws Exception { 
      SpringApplication.run(Application.class, args); 
     } 

     @Bean 
     public EmbeddedServletContainerFactory servletContainer() { 
      TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory(); 
      tomcat.addAdditionalTomcatConnectors(createSslConnector()); 
      return tomcat; 
     } 

     // ************************************************************************************************* 
     // Mutual Cert Authentication 
     // ************************************************************************************************* 
     private Connector createSslConnector() { 
      Connector connector = new Connector(
        "org.apache.coyote.http11.Http11NioProtocol"); 
      Http11NioProtocol protocol = (Http11NioProtocol) connector 
        .getProtocolHandler(); 
      try { 
       File keystore = new ClassPathResource("server.jks").getFile(); 
       File truststore = new ClassPathResource("cacerts.jks").getFile(); 
       connector.setScheme("https"); 
       connector.setSecure(true); 
       connector.setPort(8443); 
       protocol.setSSLEnabled(true); 
       protocol.setKeystoreFile(keystore.getAbsolutePath()); 
       protocol.setKeystorePass("toor"); //example password 
       protocol.setTruststoreFile(truststore.getAbsolutePath()); 
       protocol.setTruststorePass("toor"); //example passsword 
       protocol.setKeyAlias("server"); 
       protocol.setClientAuth("want"); 
       protocol.setSslProtocol("TLS"); 

       return connector; 
      } catch (IOException ex) { 
       throw new IllegalStateException("can't access keystore: [" 
       + "keystore" + "] or truststore: [" + "keystore" + "]", ex); 
      } 
     } 

     // ************************************************************************************************* 
     // The Authentication Manager Bean provides the source that userdata gets 
     // authenticated against. In this Scenario a ldap server is used. 
     // ************************************************************************************************* 
     @Bean 
     public DefaultSpringSecurityContextSource getSource() throws Exception { 

      String address = "ldap://lokalhost:389/dc=ldap"; //example url 
      String ldapUser = "cn=admin,dc=ldap";    //example login 
      String ldapPassword = "toor";      //example password 

      DefaultSpringSecurityContextSource source = new DefaultSpringSecurityContextSource(
       address); 
      source.setUserDn(ldapUser); 
      source.setPassword(ldapPassword); 
      source.afterPropertiesSet(); 
      return source; 
     } 
    } 

網/配置/ WebSecurity.java

 @Configuration 
     @EnableWebSecurity 
     public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 


       @Autowired 
       private DefaultSpringSecurityContextSource source; 

       @Autowired 
       public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 

        auth.ldapAuthentication().contextSource(source) 
          .userSearchBase("dc=users,dc=ldap") 
          .userDnPatterns("cn={0},dc=users") 
          .groupSearchBase("ou=groups") 
          ; 
       } 


      @Override 
      protected void configure(HttpSecurity http) throws Exception { 
       // ************************************************************************************************* 
       // Insert pages that need propper authentication/authorization here 
       // ************************************************************************************************* 
       http 
       .x509().subjectPrincipalRegex("CN=(.*?),").and()  
       .authorizeRequests() 
       .antMatchers("/**") 
       .access("hasRole('ROLE_USER')") 
       .and() 
       .csrf().disable(); 

      } 
     } 

pom.xml中

<?xml version="1.0" encoding="UTF-8"?> 
    <project xmlns="http://maven.apache.org/POM/4.0.0"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
     <modelVersion>4.0.0</modelVersion> 

     <groupId>SpringCertAuth</groupId> 
     <artifactId>spring-cert-authentication</artifactId> 
     <version>0.1.0</version> 

     <parent> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-parent</artifactId> 
      <version>1.2.5.RELEASE</version> 
     </parent> 

     <dependencies> 
      <!-- ldap --> 
     <dependency> 
      <groupId>org.springframework.security</groupId> 
      <artifactId>spring-security-ldap</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>org.apache.directory.server</groupId> 
      <artifactId>apacheds-server-jndi</artifactId> 
      <version>1.5.5</version> 
     </dependency> 
     <!-- end ldap --> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-security</artifactId> 
     </dependency> 

     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-tomcat</artifactId> 
      <scope>provided</scope> 
     </dependency> 
     <dependency> 
      <groupId>org.apache.tomcat.embed</groupId> 
      <artifactId>tomcat-embed-jasper</artifactId> 
      <scope>provided</scope> 
     </dependency> 
     <dependency> 
      <groupId>javax.servlet</groupId> 
      <artifactId>jstl</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-web</artifactId> 
     </dependency>  

     <properties> 
      <main.basedir>${basedir}/../..</main.basedir> 
      <java.version>1.8</java.version> 
     </properties> 

     <build> 
      <plugins> 
       <plugin> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-maven-plugin</artifactId> 
       </plugin> 
      </plugins> 
     </build>  
    </project> 

網頁/控件ER/HomeController.java

 @Controller 
     public class HomeController { 

      @RequestMapping("/welcome") 
      public ModelAndView index() { 
       ModelAndView model = new ModelAndView(); 
       model.addObject("title","Secure Web Application"); 
       model.addObject("message", "this is the welcome page"); 
       model.setViewName("welcome");  
       return model;  
     } 
    } 

而且web應用/ WEB-INF/JSP /的welcome.jsp

<%@page session="false"%> 
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 

    <html> 
    <body> 
     <h1>Title : ${title}</h1> 
     <h1>Message : ${message}</h1> 
    </body> 
    </html> 

PS:我使用的證書是自簽署並趴在src /主/資源文件夾。

我希望有人能幫助我。

問候 多米尼克

回答

0

OK,我找到了解決辦法。我將應用程序類重寫爲:

. 
. 
. 
    public static DefaultSpringSecurityContextSource getSource() throws Exception { 

     String address = "ldap://lokalhost:389/dc=ldap"; //example url 
     String ldapUser = "cn=admin,dc=ldap";    //example login 
     String ldapPassword = "toor";      //example password 

     DefaultSpringSecurityContextSource source = new DefaultSpringSecurityContextSource(
       address); 
     source.setUserDn(ldapUser); 
     source.setPassword(ldapPassword); 
     source.afterPropertiesSet(); 
     return source; 
    } 

    @Bean 
    public static LdapAuthenticationProvider ldapAuthProvider() throws Exception{ 

     LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator(),authPopulator()); 

     return provider; 
    } 


    @Bean 
    public static BindAuthenticator authenticator() throws Exception{ 
     String[] userDn = {"cn={0},dc=users"}; 

     BindAuthenticator auth = new BindAuthenticator(getSource()); 
     auth.setUserDnPatterns(userDn); 
     return auth; 

    } 
    //authenticator2 only neccessary if authentiction with passwordcompare instead of binduser is wanted. 
    @Bean 
    public static PasswordComparisonAuthenticator authenticator2() throws Exception{ 
     String[] userDn = {"cn={0},dc=users"}; 
     PasswordComparisonAuthenticator auth = new PasswordComparisonAuthenticator(getSource()); 
     auth.setUserDnPatterns(userDn); 
     auth.setPasswordAttributeName("userPassword"); 
     auth.setPasswordEncoder(Md5Encoder()); 

     return auth; 

    } 

    @Bean 
    public static DefaultLdapAuthoritiesPopulator authPopulator() throws Exception{ 

     DefaultLdapAuthoritiesPopulator authPop = new DefaultLdapAuthoritiesPopulator(getSource(),"dc=groups"); 
     authPop.setGroupRoleAttribute("cn"); 
     authPop.setGroupSearchFilter("(member={0})"); 
     return authPop; 
    } 

    //Certificate Authentication 
    @Bean 
    public static LdapUserDetailsService CustomLdapUserDetailsService() throws Exception{ 
     LdapUserDetailsService userDetails = new LdapUserDetailsService(userSearch(),authPopulator()); 
     return userDetails; 

    } 
    @Bean 
    public static FilterBasedLdapUserSearch userSearch() throws Exception{ 
     FilterBasedLdapUserSearch search = new FilterBasedLdapUserSearch("","cn={0}",getSource()); 
     return search;  
    } 
} 

另外我稍微更改了WebSecurityConfig類。現在,它看起來像這樣:

@Configuration 
@EnableWebSecurity 
@EnableAutoConfiguration 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 


    @Autowired 
    public void configure(AuthenticationManagerBuilder auth) throws Exception{ 

     auth.authenticationProvider(Application.ldapAuthProvider()); 

    } 


    @Override 
     public void configure(WebSecurity web) throws Exception { 
      web 
       .ignoring() 
        .antMatchers("/resources/**"); 
     } 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     // ************************************************************************************************* 
     // Insert pages that need proper authentication/authorization here 
     // ************************************************************************************************* 
     http 
     .exceptionHandling().accessDeniedPage("/403") 
     .and() 
     .x509().subjectPrincipalRegex("CN=(.*?),").userDetailsService(Application.CustomLdapUserDetailsService()) 
     .and() 
     .authorizeRequests() 
     .antMatchers("/profile/**").access("hasRole('ROLE_VIEW') or hasRole('ROLE_ADMINISTRATOR')") 
     .antMatchers("/welcome**").permitAll() 
     .antMatchers("/authenticate").access("hasRole('ROLE_VIEW') or hasRole('ROLE_ADMIN')") 
     .antMatchers("/admin").access("hasRole('ROLE_ADMINISTRATOR')") 
     .and() 
     .formLogin()   
     .and() 
     .logout().logoutSuccessUrl("/welcome?logout").logoutUrl("/logout") 
     .deleteCookies("JSESSIONID")   
     .and() 
     .csrf().disable() 

     ;   
    } 
} 

最後的線索給了我這個帖子在這裏: spring-security : Using user's certificate to authenticate against LDAP

我希望我可以幫助別人這一點。

Regards Dominik