복붙노트

[SPRING] Ldap 인증 및 데이터베이스 권한에 대한 스프링 보안 스위치

SPRING

Ldap 인증 및 데이터베이스 권한에 대한 스프링 보안 스위치

웹 페이지 및 웹 서비스에 대한 데이터베이스 인증을 구현했습니다. 그것은 둘 다 잘 작동하며 이제 Ldap 인증을 추가해야합니다. 원격 Ldap 서버 (사용자 이름과 암호 사용)를 통해 인증해야하며 사용자가 존재하면 사용자 역할에 데이터베이스를 사용해야합니다 (데이터베이스 사용자 이름은 Ldap과 동일한 사용자 이름 임). 그래서 위의 설명과 같이 실제 코드에서 Ldap 및 데이터베이스 인증으로 전환해야합니다. 내 코드는 다음과 같습니다. SecurityConfig 클래스

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("userDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

    @Configuration
    @Order(1)
    public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
        @Override
        protected void configure(HttpSecurity http) throws Exception {
             http.csrf().disable()
             .antMatcher("/client/**")
             .authorizeRequests()
             .anyRequest().authenticated()
             .and()
             .httpBasic();
        }
    }

    @Configuration
    @Order(2)
    public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter{

        @Override
        public void configure(WebSecurity web) throws Exception {
            web
            //Spring Security ignores request to static resources such as CSS or JS files.
            .ignoring()
            .antMatchers("/static/**");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
            .authorizeRequests() //Authorize Request Configuration
                //the / and /register path are accepted without login
                //.antMatchers("/", "/register").permitAll()
                //the /acquisition/** need admin role
                //.antMatchers("/acquisition/**").hasRole("ADMIN")
                //.and().exceptionHandling().accessDeniedPage("/Access_Denied");
                //all the path need authentication
                .anyRequest().authenticated()
                .and() //Login Form configuration for all others
            .formLogin()
                .loginPage("/login")
                //important because otherwise it goes in a loop because login page require authentication and authentication require login page
                    .permitAll()
            .and()
            .logout()
                .logoutSuccessUrl("/login?logout")
                .permitAll();
             // CSRF tokens handling
        }
    }

MyUserDetailsService 클래스

@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserServices userServices;
    static final Logger LOG = LoggerFactory.getLogger(MyUserDetailsService.class);

    @Transactional(readOnly=true)
    @Override
    public UserDetails loadUserByUsername(final String username){
        try{
            com.domain.User user = userServices.findById(username);
            if (user==null)
                LOG.error("Threw exception in MyUserDetailsService::loadUserByUsername : User doesn't exist" ); 
            else{
                List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());
                return buildUserForAuthentication(user, authorities);
            }
        }catch(Exception e){
            LOG.error("Threw exception in MyUserDetailsService::loadUserByUsername : " + ErrorExceptionBuilder.buildErrorResponse(e));  }
        return null;
    }

    // Converts com.users.model.User user to
    // org.springframework.security.core.userdetails.User
    private User buildUserForAuthentication(com.domain.User user, List<GrantedAuthority> authorities) {
        return new User(user.getUsername(), user.getPassword(), user.isEnabled(), true, true, true, authorities);
    }

    private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {

        Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();

        // Build user's authorities
        for (UserRole userRole : userRoles) {
            setAuths.add(new SimpleGrantedAuthority(userRole.getUserRoleKeys().getRole()));
        }

        List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);

        return Result;
    }

그래서 나는해야한다 :

1) 웹 페이지의 로그인 페이지와 웹 서비스의 사용자 이름 및 암호에서 사용자 액세스. 이 작업은 Ldap을 통해 수행해야합니다.

2) 사용자를 인증하기 위해 데이터베이스 쿼리에 필요한 사용자 이름 이걸 어떻게 구현할 수 있는지 알고 있습니까? 감사

오른쪽 코드로 업데이트 : @M 다음. Deinum advice MyUserDetailsService 대신 MyAuthoritiesPopulator 클래스를 만들고 데이터베이스와 Ldap 인증을 사용합니다.

    @Service("myAuthPopulator")
public class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator {

    @Autowired
    private UserServices userServices;
    static final Logger LOG = LoggerFactory.getLogger(MyAuthoritiesPopulator.class);

    @Transactional(readOnly=true)
    @Override
    public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
        try{
            com.domain.User user = userServices.findById(username);
            if (user==null)
                LOG.error("Threw exception in MyAuthoritiesPopulator::getGrantedAuthorities : User doesn't exist into ATS database" );  
            else{
                for(UserRole userRole : user.getUserRole()) {
                    authorities.add(new SimpleGrantedAuthority(userRole.getUserRoleKeys().getRole()));
                }
                return authorities;
            }
        }catch(Exception e){
            LOG.error("Threw exception in MyAuthoritiesPopulator::getGrantedAuthorities : " + ErrorExceptionBuilder.buildErrorResponse(e)); }
        return authorities;
    }
}

아래와 같이 SecurityConfig를 변경했습니다.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("myAuthPopulator")
    LdapAuthoritiesPopulator myAuthPopulator;

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

         auth.ldapAuthentication()
          .contextSource()
            .url("ldap://127.0.0.1:10389/dc=example,dc=com")
//          .managerDn("")
//          .managerPassword("")
          .and()   
            .userSearchBase("ou=people")
            .userSearchFilter("(uid={0})")
            .ldapAuthoritiesPopulator(myAuthPopulator);     
    }

    @Configuration
    @Order(1)
    public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
        @Override
        protected void configure(HttpSecurity http) throws Exception {
             http.csrf().disable()
             .antMatcher("/client/**")
             .authorizeRequests()
             //Excluede send file from authentication because it doesn't work with spring authentication
             //TODO add java authentication to send method
             .antMatchers(HttpMethod.POST, "/client/file").permitAll()
             .anyRequest().authenticated()
             .and()
             .httpBasic();
        }
    }

    @Configuration
    @Order(2)
    public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter{

        @Override
        public void configure(WebSecurity web) throws Exception {
            web
            //Spring Security ignores request to static resources such as CSS or JS files.
            .ignoring()
            .antMatchers("/static/**");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
            .authorizeRequests() //Authorize Request Configuration
                //the "/" and "/register" path are accepted without login
                //.antMatchers("/", "/register").permitAll()
                //the /acquisition/** need admin role
                //.antMatchers("/acquisition/**").hasRole("ADMIN")
                //.and().exceptionHandling().accessDeniedPage("/Access_Denied");
                //all the path need authentication
                .anyRequest().authenticated()
                .and() //Login Form configuration for all others
            .formLogin()
                .loginPage("/login")
                //important because otherwise it goes in a loop because login page require authentication and authentication require login page
                    .permitAll()
            .and()
            .logout()
                .logoutSuccessUrl("/login?logout")
                .permitAll();
        }
    }
}

Apache 디렉토리 스튜디오에서 만든 LDAP 개발 환경

해결법

  1. ==============================

    1.스프링 시큐리티는 이미 out-of-the-box LDAP를 지원한다. 실제로 이것에 관한 전체 장을 가지고 있습니다.

    스프링 시큐리티는 이미 out-of-the-box LDAP를 지원한다. 실제로 이것에 관한 전체 장을 가지고 있습니다.

    LDAP를 사용하고 구성하려면 spring-security-ldap 종속성을 추가 한 다음 AuthenticationManagerBuilder.ldapAuthentication을 사용하여 구성하십시오. LdapAuthenticationProviderConfigurer를 사용하면 필요한 것을 설정할 수 있습니다.

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.ldapAuthentication()
          .contextSource()
            .url(...)
            .port(...)
            .managerDn(...)
            .managerPassword(...)
          .and()
            .passwordEncoder(passwordEncoder())
            .userSearchBase(...)        
            .ldapAuthoritiesPopulator(new UserServiceLdapAuthoritiesPopulater(this.userService));      
    }
    

    뭔가 (일을 구성하는 방법에 대한 아이디어를 적어도 제공해야합니다) 거기에는 더 많은 옵션이 있지만 javadocs를 확인하십시오. UserService를 그대로 사용하여 역할을 검색 할 수 없다면 (데이터베이스에 역할 만 있기 때문에) LdapAuthoritiesPopulator를 구현하십시오.

  2. ==============================

    2.AuthenticationProvider를 구현하는 CustomAuthenticationProvider를 생성하고 authenticate 메소드를 재정의해야합니다. 예를 들면 다음과 같습니다.

    AuthenticationProvider를 구현하는 CustomAuthenticationProvider를 생성하고 authenticate 메소드를 재정의해야합니다. 예를 들면 다음과 같습니다.

    @Component
    public class CustomAuthenticationProvider
        implements AuthenticationProvider {
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            String username = authentication.getName();
            String password = authentication.getCredentials().toString();
    
            boolean authenticated = false;
            /**
             * Here implements the LDAP authentication
             * and return authenticated for example
             */
            if (authenticated) {
    
                String usernameInDB = "";
                /**
                 * Here look for username in your database!
                 * 
                 */
                List<GrantedAuthority> grantedAuths = new ArrayList<>();
                grantedAuths.add(new     SimpleGrantedAuthority("ROLE_USER"));
                Authentication auth = new     UsernamePasswordAuthenticationToken(usernameInDB, password,     grantedAuths);
                return auth;
            } else {
                return null;
            }
        }
    
        @Override
        public boolean supports(Class<?> authentication) {
            return     authentication.equals(UsernamePasswordAuthenticationToken.class);
        }
    
    }
    

    그런 다음 SecurityConfig에서 AuthenticationManagerBuilder를 사용하는 구성을 재정의해야합니다.

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(this.authenticationProvider);
    }
    

    다음과 같이 CustomAuthenticationProvider를 autowire 할 수 있습니다.

    @Autowired
    private CustomAuthenticationProvider authenticationProvider;
    

    이렇게하면 기본 인증 동작을 재정의 할 수 있습니다.

  3. ==============================

    3.또한이 장 (章)에서 Docu Custom Authority를 ​​발견하고 LDAP와 내 DB 사용자간에 직접 스위치를 구축했습니다. 나는 우선 순위가 설정된 로그인 데이터 사이를 쉽게 전환 할 수있다 (나의 경우 LDAP 승리).

    또한이 장 (章)에서 Docu Custom Authority를 ​​발견하고 LDAP와 내 DB 사용자간에 직접 스위치를 구축했습니다. 나는 우선 순위가 설정된 로그인 데이터 사이를 쉽게 전환 할 수있다 (나의 경우 LDAP 승리).

    LDAP 사용자 데이터에 대한 yaml 구성 파일을 사용하여 LDAP를 구성했습니다.이 파일은 여기서 자세히 설명하지 않습니다. 이것은 Spring Docu LDAP Configuration으로 쉽게 할 수 있습니다.

    중요한 부분을 강조하기 위해 logger / javadoc 등과 같은 불안정한 부분에서 다음 예제를 제거했습니다. @Oder annotation은 로그인 데이터가 사용되는 우선 순위를 결정합니다. 메모리 내 세부 사항은 dev 전용 목적으로 하드 코드 된 디버그 사용자입니다.

    SecurityWebConfiguration

    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
      @Inject
      private Environment env;
      @Inject
      private LdapConfiguration ldapConfiguration;
    
      @Inject
      private BaseLdapPathContextSource contextSource;
      @Inject
      private UserDetailsContextMapper userDetailsContextMapper;
    
      @Inject
      private DBAuthenticationProvider dbLogin;
    
      @Inject
      @Order(10) // the lowest number wins and is used first
      public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(new InMemoryUserDetailsManager(getInMemoryUserDetails()));
      }
    
      @Inject
      @Order(11) // the lowest number wins and is used first
      public void configureLDAP(AuthenticationManagerBuilder auth) throws Exception {
        if (ldapConfiguration.isLdapEnabled()) {
          auth.ldapAuthentication().userSearchBase(ldapConfiguration.getUserSearchBase())
              .userSearchFilter(ldapConfiguration.getUserSearchFilter())
              .groupSearchBase(ldapConfiguration.getGroupSearchBase()).contextSource(contextSource)
              .userDetailsContextMapper(userDetailsContextMapper);
        }
      }
    
      @Inject
      @Order(12) // the lowest number wins and is used first
      public void configureDB(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(dbLogin);
      }
    }
    

    DB 인증 프로그램

    @Component
    public class DBAuthenticationProvider implements AuthenticationProvider {
    
      @Override
      public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();
    
       // your code to compare to your DB
      }
    
      @Override
      public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
      }
    
      /**
       * @param original <i>mandatory</i> - input to be hashed with SHA256 and HEX encoding
       * @return the hashed input
       */
      private String sha256(String original) {
        MessageDigest md = null;
        try {
          md = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
          throw new AuthException("The processing of your password failed. Contact support.");
        }
    
        if (false == Strings.isNullOrEmpty(original)) {
          md.update(original.getBytes());
        }
    
        byte[] digest = md.digest();
        return new String(Hex.encodeHexString(digest));
      }
    
      private class AuthException extends AuthenticationException {
        public AuthException(final String msg) {
          super(msg);
        }
      }
    }
    

    언제든지 세부 사항을 물어보십시오. 나는 이것이 다른 누군가에게 유용하길 바란다 : D

  4. ==============================

    4.grails를 사용하는 사람은 훨씬 더 간단합니다. 이 설정을 설정에 추가하기 만하면됩니다.

    grails를 사용하는 사람은 훨씬 더 간단합니다. 이 설정을 설정에 추가하기 만하면됩니다.

    grails :   플러그인:     springsecurity :       LDAP :         당국:            retrieveDatabaseRoles : true

  5. from https://stackoverflow.com/questions/34658534/spring-security-switch-to-ldap-authentication-and-database-authorities by cc-by-sa and MIT license