[데브코스] Spring Security - 5

Updated: Categories:

W16D4 - log4jdbc / Spring Security - JDBC 연동에 대해 알아보자

log4jdbc-remix

  • 실행되는 SQL 및 ResultSet을 로깅해주는 라이브러리
  • BeanPostProcessor 인터페이스를 구현하여, DataSource 객체를 Log4jdbcProxyDataSource 타입으로 Wrapping 처리
DataSourcePostProcessor
@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;
        }
    }
}
  • logback 설정을 통해 선택적으로 로깅 처리
logback.xml
<logger name="jdbc.sqltiming" level="INFO"/>
<logger name="jdbc.resultsettable" level="INFO"/>
<logger name="jdbc.audit" level="OFF"/>
<logger name="jdbc.resultset" level="OFF"/>
<logger name="jdbc.connection" level="OFF"/>
<logger name="jdbc.sqlonly" level="OFF"/>
  • 아래처럼 sqltimingresultsettable 설정이 잘 뜨는걸 볼 수 있다!

image


JDBC와 연동

  • 사용자를 인증할때 UserDetailsService 인터페이스를 사용하게 된다.
  • 구현체 중 JDBC를 지원하는 JdbcDaoImpl 클래스를 사용하면 인증과정을 DB와 연동할 수 있음
    • 단 반드시 테이블이 다음과 같은 구조를 가져야함.
CREATE TABLE users
(
    username varchar(20) NOT NULL,
    password varchar(80) NOT NULL,
    enabled  varchar(20) NOT NULL DEFAULT false,
    PRIMARY KEY (username)
);

CREATE TABLE authorities
(
    username varchar(20) NOT NULL,
    authority varchar(20) NOT NULL,
    PRIMARY KEY (username)
);
  • 이제 DataSource를 UserDetailsService를 통해 연동해주면 된다
WebSecurityConfig 클래스
@Bean
public UserDetailsService userDetailsService(DataSource dataSource) {
    JdbcDaoImpl jdbcDao = new JdbcDaoImpl();
    jdbcDao.setDataSource(dataSource);
    return jdbcDao;
}
  • UserDetailsServiceJdbcDaoImpl 구현체에 DB를 연동해주고 빈으로 띄워준다

Permission & Group

  • 사용자와 권한 사이에 그룹이라는 간접 계층을 둘 수 있음
    • 사용자는 특정 그룹에 속하게 되고, 그룹은 권한 집합을 참조함
    • 즉, 사용자를 특정 그룹에 속하게 함으로써, 그룹에 속한 권한을 일괄 적용할 수 있음

image

  • 위와 같은 의존 관계를 적용해주기 위해선 JdbcDaoImpl의 기본 쿼리를 바꿔줘야 한다.
    • usersByUsernameQuery : username으로 해당 유저를 찾는다
    • groupAuthoritiesByUsernameQuery : username으로 그룹 권한을 가져온다.
@Bean
public UserDetailsService userDetailsService(DataSource dataSource) {
    JdbcDaoImpl jdbcDao = new JdbcDaoImpl();
    jdbcDao.setDataSource(dataSource);
    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 = ?"
    );
    jdbcDao.setEnableAuthorities(false);
    jdbcDao.setEnableGroups(true);
    return jdbcDao;
}
  • 하지만 이렇게 UserDetailsService를 커스텀하여 적용하는 것은 정석적인 방법이 아니다!!
    • RememberMe 인증에서 Exception이 발생하게 됨.
  • 따라서 아래와 같이 AuthenticationManagerBuilder를 사용해 JdbcDaoImpl 클래스를 커스텀해줄 수 있다.
WebSecurityConfig 클래스
private DataSource dataSource;

@Autowired
public void setDataSource(DataSource dataSource){
    this.dataSource = dataSource;
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.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)
    ;
}