2022년 5월 23일 TIL

yshjft·2022년 5월 23일
0

데브코스 TIL

목록 보기
37/45

데이터베이스를 사용한 인증 적용

H2 이용시 Spring Security 설정

@Override
public void configure(WebSecurity web) {
  web.ignoring().antMatchers("/assets/**", "/h2-console/**");
}

h2 데이터베이스의 console을 사용할 수 있도록 security 필터를 거치지 않도록 설정해주어야 한다.

H2 데이테베이스 초기화

spring:
	sql:
    init:
      platform: h2
      schema-locations: classpath:sql/schema.sql
      data-locations: classpath:sql/data.sql
      encoding: UTF-8

log4jdbc-remix 적용

@Component
public class DataSourcePostProcessor implements BeanPostProcessor {

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof DataSource && !(bean instanceof Log4jdbcProxyDataSource)) {
      return new Log4jdbcProxyDataSource((DataSource) bean);
    } else {
      return bean;
    }
  }

}
  • 실행되는 SQL 및 ResultSet을 로깅
  • BeanPostProcessor 인터페이스를 구현하여, DataSource 객체를 Log4jdbcProxyDataSource 타입으로 Wrapping 처리
public class Log4jdbcProxyDataSource implements DataSource {
	getConnection() throws SQLException {
		Connection connection = this.realDataSource.getConnection();

		// 데코레이터 패턴 이용
		return new ConnectionSpy(connection, DriverSpy.getRdbmsSpecifics(connection)); 
	}
}
  • ConnectionSpy: 로그를 남길 수 있는 기능 부여

데이터베이스 기반 인증 처리

인증 처리의 시작은 filter

  • UsernamePasswordAuthenitcationFilter, RememberMeAutheticationFilter
  • AuthenticationManager에 의존하고 있다.
  • AuthenticationManager에.authenticate()을 통해 인증을 진행한다.

ProviderManager

  • AuthenticationManager의 기본 구현체
  • AuthenticationProvider 목록을 가지고 있다.
  • 사용자의 인증 처리를 위한 작업을 AuthenticationProvider로 위임한다.
  • UsernamePasswordAuthenticationToken 타입의 인증 요청은 DaoAuthenticationProvider가 처리한다.

AuthenticationProvider

  • 인터페이스이며 DaoAuthenticationProvider 가장 일반적으로 사용되는 구현체이다.

DaoAuthenticationProvider

  • retrieveUser(): 사용자를 조회해오는 역할을 한다.
    • UserDetailsService 인터페이스의 loadUserByUsername()을 통해 사용자를 조회한다.

UserDetailsService

  • 인터페이스
  • InMemoryUserDetailsManager는 기본 구현체이다.
  • 데이터베이스를 이용한 인증처리를 하기 위해서는 JdbcDaoImpl을 사용해야한다.

jdbcAuthentication()

  • UserDetailsService 인터페이스 구현체로 JdbcUserDetailsManager 객체를 등록한다.
  • JdbcUserDetailsManager 클래스는 JdbcDaoImpl 클래스를 상속하며, 보다 다양한 기능을 제공한다.

inMemoryAuthentication()

UserDetailsService 인터페이스 구현체로 InMemoryUserDetailsManager 객체를 등록한다.

loadUserByUsername()

loadUserByUsername()를 이용하여 DB에서 사용자 인증 정보를 가져올 수 있다.

JdbcDaoImpl 커스텀 설정

  • 쿼리를 테이블 구조에 맞춰 수정해야한다.
  • JdbcDaoImpl에서 사용하는 쿼리가 달라져야 한다

JdbcDaoImpl에서 사용하는 쿼리

usersByUsernameQuery

  • loadUsersByUsername(String username)에서 사용
  • 사용자명과 일치하는 하나 이상의 사용자를 조회한다.
  • username, password, enabled(계정의 활성화 여부)에 해당하는 데이터가 순서대로 필요하다.

authoritiesByUsernameQuery

  • loadUserAuthorities(String username)에서 사용
  • 사용자에게 직접 부여된 하나 이상의 권한을 반환한다.
  • 조회하는 두 번째 값은 반드시 authority: String 컬럼이어야 한다.

groupAuthoritiesByUsernameQuery

  • loadGroupAuthorities(String username)에서 사용
  • 그룹 멤버십을 통해 사용자에게 승인된 권한을 반환한다.
  • 조회하는 세 번째 값은 반드시 authority: String 컬럼이어야 한다.

직접 Bean으로 등록하여 설정

@Bean
public UserDetailsService userDetailsService(DataSource dataSource) {
    JdbcDaoImpl jdbcDao = new JdbcDaoImpl();
    jdbcDao.setDataSource(dataSource);
    jdbcDao.setEnableAuthorities(false);
    jdbcDao.setEnableGroups(true);
    jdbcDao.setUsersByUsernameQuery(
            "SELECT " +
               "login_id, passwd, true " +
            "FROM " +
               "USERS " +
            "WHERE " +
               "login_id = ?"
    );
    jdbcDao.setGroupAuthoritiesByUsernameQuery(
            "SELECT " +
              "u.login_id, g.name, p.name " +
            "FROM " +
              "users u JOIN groups g ON u.group_id = g.id " +
              "LEFT JOIN group_permission gp ON g.id = gp.group_id " +
              "JOIN permissions p ON p.id = gp.permission_id " +
            "WHERE " +
              "u.login_id = ?"
    );

    return jdbcDao;
}
  • Remember-me 로 로그인할 경우 제대로 작동되지 않는다.
  • UserDetailsService에 대한 참조를 정상적으로 가져오지 못한다.

가장 추천되는 방법

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
            // .userDetailsService() 직접 구현한 userDetailsService 참조를 넘겨줄 수 있다.
            .jdbcAuthentication()
            .dataSource(dataSource)
            .usersByUsernameQuery(
                    "SELECT " +
                            "login_id, passwd, true " +
                            "FROM " +
                            "USERS " +
                            "WHERE " +
                            "login_id = ?"
            )
            .groupAuthoritiesByUsername(
                    "SELECT " +
                            "u.login_id, g.name, p.name " +
                            "FROM " +
                            "users u JOIN groups g ON u.group_id = g.id " +
                            "LEFT JOIN group_permission gp ON g.id = gp.group_id " +
                            "JOIN permissions p ON p.id = gp.permission_id " +
                            "WHERE " +
                            "u.login_id = ?"
            )
            .getUserDetailsService().setEnableAuthorities(false)
    ;
}
profile
꾸준히 나아가자 🐢

0개의 댓글