백엔드 데브코스 TIL 44일차

Inchang Choi·2022년 6월 7일
0

백엔드 데브코스 TIL

목록 보기
28/30
post-thumbnail

학습목표

강의를 들으며 내가 알고 있는 내용을 점검하고,

새로 배운 내용을 정리하며,

궁금한 내용을 알아가며 학습해나가는 것을 목표로 합니다.

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

Spring Security를 실제 서비스에 적용하기 위해서는 사용자 로그인 정보를 Java Configuration을 통해 관리 하는 것이 아닌 사용자 로그인 정보를 데이터베이스에서 관리할수 있도록 변경해야 할 필요가 있습니다.

인증 톺아보기

AuthenticationManager는 사용자의 인증 처치를 위한 작업을 AuthenticationProvider로 위임합니다.

UsernamePasswordAuthenticationToken 타입의 인증 요청은 DaoAuthenticationProvider가 처리합니다.

DaoAuthenticationProvider

데이터베이스에서 사용자 인증 정보를 조회하는 작업을 UserDetailsService 인터페이스 구현체에 위임합니다.

지금까지 우리는 UserDetailsService 인터페이스 구현체 중 InMemoryUserDetailsManager 클래스를 사용하였습니다.
즉 JDBC를 지원하는 org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl 구현체를 사용할 수 있습니다.

JdbcDaoImpl

이름 그대로 JDBC를 통해 데이터베이스에서 사용자 인증 정보를 가져옵니다.

select username, password, enabled from users where username = ? -- 사용자 조회
select username, authority from authorities where username = ?  -- 사용자의 권한 조회

schema.sql 에서 제공하는 기본 테이블 구조가 JdbcDaoImpl 클래스에서 사용하는 쿼리에 적합하게 된 것은 우연의 일치가 아닙니다.

  • jdbcAuthentication 메소드는 UserDetailsService 인터페이스 구현체로 JdbcUserDetailsManager 객체를 등록합니다.
  • JdbcUserDetailsManager 클래스는 JdbcDaoImpl 클래스를 상속하며, 보다 풍부한 기능을 제공합니다.

⚠️ inMemoryAuthentication 메소드는 UserDetailsService 인터페이스 구현체로 InMemoryUserDetailsManager 객체를 등록했었다.

JdbcDaoImpl 객체를 Bean으로 등록하면 데이터베이스 기반 인증 처리가 완료됩니다.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth.jdbcAuthentication()
    .dataSource(dataSource)
  ;
}

데이터베이스 기반 인증 고급 설정

대부분의 경우 UserDetailsService의 커스텀 구현체를 만들지 않더라도 JdbcDaoImpl 클래스를 설정하여 대응할 수 있습니다.

JdbcDaoImpl 클래스는 기존 데이터베이스 스키마에 적용하거나 기존 기능을 더욱 정교하게 설정할 수 있도록 다양한 옵션을 제공합니다.

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

💡 sql 디렉토리 아래에 쿼리 파일들을 Group-based Access Control 기능을 사용할 있도록 데이터베이스에 테이블을 생성하고, 필요한 데이터를 입력한다.

JdbcDaoImpl 클래스는 수행 목적에 따라 3개의 SQL 쿼리를 정의하고 있는데 이를 위 테이블 구조에 맞게 재정의하여 활용해야 합니다.

  • usersByUsernameQuery
    • 사용자명과 일치하는 하나 이상의 사용자를 조회

    • 조회하는 값들은 반드시 username: String, password: String, enabled: Boolean 컬럼 순서여야 합니다.

      select username, password, enabled from users where username = ?
  • authoritiesByUsernameQuery
    • 사용자에게 직접 부여된 하나 이상의 권한을 반환 (Group-based Access Control 미적용시)

    • 조회하는 두 번째 값은 반드시 authority: String 컬럼 이어야 합니다.

      select username, authority from authorities where username = ?
  • groupAuthoritiesByUsernameQuery
    • 그룹 멤버십을 통해 사용자에게 승인된 권한을 반환 (Group-based Access Control 적용시)

    • 조회하는 세 번째 값은 반드시 authority: String 컬럼 이어야 합니다.

      select g.id, g.group_name, ga.authority 
      from groups g, group_members gm, group_authorities ga 
      where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id
  • JdbcDaoImpl 객체를 Bean으로 등록
    • usersByUsernameQuery, groupAuthoritiesByUsernameQuery SQL 쿼리 재정의

    • enableGroups — Group-based Access Control 활용시 true 입력

      • groupAuthoritiesByUsername 쿼리 정의시 자동으로 true 설정됩니다.
    • enableAuthorities — Group-based Access Control 활용시 false 입력

      @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)
        ;
      }
profile
always positive

0개의 댓글