[8/7 TIL] SPRING SECURITY(h2 데이터베이스 사용, JdbcDaoImpl, JdbcUserDetailsManager, JPA 사용)

yumyeonghan·2023년 8월 8일
0

🍃프로그래머스 백엔드 데브코스 4기 교육과정을 듣고 정리한 글입니다.🍃

h2 데이터베이스 사용

	@Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web -> web.ignoring().requestMatchers(new AntPathRequestMatcher("/h2-console/**")));
    }
  • DataSource 및 h2 관련 의존성 추가
  • ignoring()를 설정하지 않으면 CsrfFilter에 의해 /h2-console 페이지가 막힘
  • 이전에는 인증할 때 In-Memory 방법을 선택했다면, 지금은 데이터베이스 사용

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


1. AuthenticationManager는 사용자의 인증 처치를 위한 작업을 AuthenticationProvider로 위임함
2. UsernamePasswordAuthenticationToken 타입의 인증 요청은 DaoAuthenticationProvider가 처리함
3. DaoAuthenticationProvider는 데이터베이스에서 사용자 인증 정보를 조회하는 작업을 UserDetailsService 인터페이스 구현체에 위임

  • 지금까지 우리는 UserDetailsService 인터페이스 구현체 중 InMemoryUserDetailsManager 클래스를 사용함
  • 따라서, UserDetailsService 인터페이스 구현체 중 JDBC를 지원하는 org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl 구현체를 사용하면됨

JdbcDaoImpl

  • JdbcDaoImpl 클래스에서 사용자 및 권한 조회를 위해 사용되는 기본 Query

CREATE TABLE users
(
    username varchar(20) NOT NULL,
    password varchar(80) NOT NULL,
    enabled  boolean     NOT NULL DEFAULT false,
    PRIMARY KEY (username)
);

CREATE TABLE authorities
(
    username  varchar(20) NOT NULL,
    authority varchar(20) NOT NULL,
    PRIMARY KEY (username)
);
  • JdbcDaoImpl 클래스에서 사용하는 쿼리에 적합하게 작성

이렇게 JdbcDaoImpl의 기본 쿼리에 적합한 테이블을 만든 후 해당 클래스를 빈으로 등록하면 인증 처리가 됨. 하지만 JdbcDaoImpl의 기본 쿼리에 맞춘 테이블이 아닌, 원래 만들어져 있던 테이블에 적용하려면 어떻게 할까?

JdbcUserDetailsManager

  • JdbcDaoImpl을 상속한 JdbcUserDetailsManager는 기존 데이터베이스 스키마에 적용하거나 기존 기능을 정교하게 설정 가능
  • Group-based Access Control: 사용자와 권한 사이에 그룹이라는 간접 계층을 둘 수 있음
    • 사용자는 특정 그룹에 속하게 되고, 그룹은 권한 집합을 참조함
    • 즉, 사용자를 특정 그룹에 속하게 함으로써, 그룹에 속한 권한을 일괄 적용할 수 있음
  • JdbcDaoImpl 클래스는 수행 목적에 따라 밑의 3개의 기본 SQL 쿼리를 정의하고 있는데, 이를 위 테이블 구조에 맞게 재정의하여 활용하면 됨
    • usersByUsernameQuery: 사용자명과 일치하는 하나 이상의 사용자를 조회
      • 조회하는 값들은 반드시 username: String, password: String, enabled: Boolean 컬럼 순서이어야함
    • authoritiesByUsernameQuery: Group-based Access Control 미적용시, 사용자에게 직접 부여된 하나 이상의 권한을 반환
      • 조회하는 두 번째 값은 반드시 authority: String 컬럼이어야 함
    • groupAuthoritiesByUsernameQuery: Group-based Access Control 적용시, 그룹 멤버십을 통해 사용자에게 승인된 권한을 반환
      • 조회하는 세 번째 값은 반드시 authority: String 컬럼이어야 함

JdbcUserDetailsManager 빈 등록

	@Bean
    public JdbcUserDetailsManager jdbcUserDetailsManager() {
        JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource);
        jdbcUserDetailsManager.setUsersByUsernameQuery("SELECT " +
                "login_id, passwd, true " +
                "FROM " +
                "users " +
                "WHERE " +
                "login_id = ?");
        jdbcUserDetailsManager.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 = ?");
        jdbcUserDetailsManager.setEnableAuthorities(false);
        jdbcUserDetailsManager.setEnableGroups(true);
        return jdbcUserDetailsManager;
    }
  • 직접 만든 테이블에 맞게, usersByUsernameQuery, groupAuthoritiesByUsernameQuery를 재정의
  • Group-based Access Control 적용 설정

JPA 사용

  • 실제 데이터베이스 스키마를 파악하고, 테이블에 대한 엔티티 클래스를 생성
  • JpaRepository<User, Long>을 상속한 UserRepository 인터페이스 생성
  • JdbcDaoImpl을 대체하기 위해 UserDetailsService 인터페이스를 구현한 UserService 클래스 생성
  • 이렇게 만들어진 UserService를 DaoAuthenticationProvider에 주입후 빈으로 등록

UserRepository 인터페이스

  • loginId로 이 사용자의 그룹과 그룹의 권한, 그리고 권한의 세부 정보를 함께 가져오는 Jpql 작성

  • UserRepository를 통해 조회한 User 엔티티 정보로 org.springframework.security.core.userdetails.UserDetails 객체를 만들어 반환

  • UserDetailsService와 PasswordEncoder를 통해 로그인시, 실제 인증 처리를 담당하는 DaoAuthenticationProvider에 직접 만든, UserService와 BCryptPasswordEncoder를 설정해서 빈으로 등록
profile
웹 개발에 관심 있습니다.

0개의 댓글