사용자 관리

스프링 시큐리티에서 사용자 관리는 UserDetailsService 담당한다

사용자를 찾는 역할만 한다.

 

UserDetailsManager 인터페이스를 확장해 User 대한 CUD 기능들을 지원한다.

인터페이스의 상속 관계는 인터페이스 분리 원칙의 모범 사례다.

 

조회만 필요한 경우와 조회된 사용자에 대한 조작이 필요한 경우 나눠서 알맞게 구현하면 된다.

 

UserDetailsService.loadUserByUsername() 조회 결과로 반환되는 UserDetails는 사용자에 대한 정보가 들어있다.

이중 권한에 대한 표현은 GrantedAuthority로 한다. 그리고 권한은 복수일 있어 Collection으로 다룬다.

 

사용자 기술하기

UserDetials

UserDetails을 알맞게 기술하면 스프링 시큐리티는 사용자를 인식할 있다.

헛갈릴 있어 일부러 Authentication 맞게 코맨트를 달았다.

isAccountNonExpired() 같이 이중 부정으로 보여 헷갈린다면,

is-- 4 가지 전부 true일 때 유효하다고 기억하면 된다.

만약 계정 만료, 계정 잠금, 자격 증명 만료, 계정 /활성화 기능이 필요 없다면, 단순히 구현부를 true 리턴하게 만들면 된다.

 

 

 

 

 

authentication 메서드 안에서 UserDetails 사용하고, 메서드 결과를 반환할 때는 Authentication으로 반환한다.

 

아래는 AbstractUserDetailsAuthenticationProvider의 구현부다.

 

 

메서드는 추상 메서드로 템플릿 메서드 패턴이 적용되어 있다.

 

클래스 확장 클래스는 DaoAuthenticationProvider .

메서드에서 아래 그림과 같은 관계를 구현하고 있다.

 

 

GrantedAuthority

사용자에게 허가된 권한을 반환하는 역할을 한다.

사용자에게 권한은 여러 개일 있으므로 Collection으로 관리된다.

 

 

GrantedAuthority 추상 메서드가 하나 뿐인 함수형 인터페이스지만 @FunctionalInterface 애노테이션이 붙지 않았다.

따라서 가급적 람다를 통한 객체 생성을 지양하고, 구현체를 써야 한다.

테스트 같은 간단한 구현에선 람다를 사용하는 것은 상관없다.

 

 

UserDetails 구현

최소한의 구현

 

빌더 사용

 

어떠한 방법을 사용하건 UserDetails 인터페이스 맞게 값만 채워서 리턴하면 된다.

보통은 DB에서 조회해서 값을 채우게 것이다.

 

 

JPA UserDetails 구현

하나의 UserDetails 구현체에 JPA 엔티티 책임과 UserDetails 책임 가지를 함께 구현하면 단일 책임 원칙에 위배되며, 코드가 복잡해진다.

따라서 책임을 분리해 엔티티는 엔티티만, UserDetails 역할은 UserDetails 구현체가 하도록 하면 된다.

 

JPA 엔티티 책임만

스프링 시큐리티가 원하는 UserDetails 책임만

 

JPA 사용하는 코드와 스프링 시큐리티가 사용하는 코드가 분리됐다.

 

 

 

스프링 시큐리티가 사용자를 관리하는 방법 지정

스프링이 이해하는 UserDetails 만드는 법을 알았으니,

다음은 정보를 가져오는 방법을 알아본다.

 

 

 

UserDetailsService 구현

스프링 시큐리티가 제공하는 InMemoryUserDetailsManager 착각하면 안된다.

 

 

 

 

 

JdbcUserDetailsManager 사용

JdbcUserDetailsManager 직접 데이터베이스에 접근해서 데이터를 가져온다.

이를 위해 도커엔진 위에 Mysql 설치했다.

 

사용한 버전, password

docker run --name mysql8 -e MYSQL_ROOT_PASSWORD=root -d mysql:8.0.27

 

 

스프링 부트는 classpath 경로에 schema.sql, data.sql 있으면, 부팅하면서 대신 실행해 준다.

, application.properties "spring.sql.init.mode=always" 설정돼야 실행된다.

application.properties

spring.datasource.url=jdbc:mysql://localhost/spring?useUnicode=true&serverTimezone=Asia/Seoul

spring.datasource.username=root

spring.datasource.password=root

spring.sql.init.mode=always

 

 

data.sql

INSERT INTO `spring`.`authorities` VALUES (NULL, 'scott', 'write');

INSERT INTO `spring`.`users` VALUES (NULL, 'scott', 'tiger', '1');

 

schema.sql

CREATE DATABASE spring default CHARACTER SET UTF8;

 

CREATE TABLE IF NOT EXISTS `spring`.`users` (

  `id` INT NOT NULL AUTO_INCREMENT,

  `username` VARCHAR(45) NULL,

  `password` VARCHAR(45) NULL,

  `enabled` INT NOT NULL,

  PRIMARY KEY (`id`));

CREATE TABLE IF NOT EXISTS `spring`.`authorities` (

  `id` INT NOT NULL AUTO_INCREMENT,

  `username` VARCHAR(45) NULL,

  `authority` VARCHAR(45) NULL,

  PRIMARY KEY (`id`));

그림에서 UserDetailsService JdbcUserDetailsManager이다.

 

이제 서버 기동 테스트를 한다.

 

데이터베이스 확인

 

 

통신 확인

 

 

spring-security-02-user.zip
0.09MB
spring-security-03-user.zip
0.08MB

+ Recent posts