// build.gradle
ependencies {
~ 생략 ~
// 스프링 시큐리티를 사용하기 위한 스타터 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
// 타임리프에서 스프링 시큐리티를 사용하기 위한 의존성 추가
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
// 스프링 시큐리티를 테스트하기 위한 의존성 추가
testImplementation 'org.springframework.security:spring-security-test'
}
id : BIGINT, NOT NULL, 기본키 - 일련번호, 기본키
email : VARCHAR(255), NOT NULL - 이메일
password : VARCHAR(255), NOT NULL - 패스워드
created_at : DATETIME, NOT NULL - 생성 일자
UPDATED_at : DATETIME, NOT NULL - 수정 일자
// domian - User.java
@Table(name = "users")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Long id;
@Column(name = "email", nullable = false, unique = true)
private String email;
@Column(name = "password", nullable = false)
private String password;
@Builder
public User(String email, String password, String auth) {
this.email = email;
this.password = password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority("user"));
}
@Override
public String getUsername() {
return email;
}
@Override
public String getPassword() {
return password;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
- getAuthorities() - [ 반환형 : Collection<? extends GrantedAuthority> ]
사용자가 가지고 있는 권한의 목록을 반환한다.
- getUsername() - [ 반환형 : String ]
사용자를 식별할 수 있는 사용자 이름을 반환한다. 이때 사용되는 사용자 이름은 반드시 고유해야 한다.
- getPassword() - [ 반환형 : String ]
사용자의 비밀번호를 반환한다. 이때 저장되어 있는 비밀번호는 암호화해서 저장해야 한다.
- isAccountNonExpired - [ 반환형 : boolean ]
계정이 만료되었는지 확인한다. 만료 되지 않았을 시 true를 반환한다.
- isAccountNonLocked() - [ 반환형 : boolean ]
계정이 잠금되었는지 확인한다. 잠금 되지 않았을 시 true를 반환한다.
- isCredentialsNonExpired() - [ 반환형 : boolean ]
비밀번호가 만료되었는지 확인한다. 만료 되지 않았을 시 true를 반환한다.
- isEnabled() - [ 반환형 : boolean ]
계정이 사용 가능한지 확인한다. 사용 가능할 시 true를 반환한다.
// repository - UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
이메일로 사용자 정보를 가져온다.
- findByEmail() 메서드가 요청하는 쿼리 예시
FROM users WHERE email = #{email}
스프링 데이터 JPA는 메서드 규칙에 맞게 메서드 선언 시 이름을 분석해 자동으로 쿼리를 생성해준다.
cf. ?1, ?2, ... , ?n : 메서드의 n번째 파라미터 의미.
- 주요 쿼리 메서드 명명 규칙
- findByName() - [ 쿼리 : ~ WHERE name=?1 ]
"name" 컬럼의 값 중 파라미터로 들어오는 값과 같은 데이터 반환.
- findByNameAndAge() - [ 쿼리 : ~ WHERE name=?1 AND age=?2 ]
파라미터로 들어오는 값 중 첫 번째 값은 "name"컬럼에서 조회하고, 두 번째 값은 "age" 컬럼에서 조회한 데이터 반환.
- findByNameOrAge() - [ 쿼리 : ~ WHERE name=?1 OR age=?2 ]
파라미터로 들어오는 값 중 첫 번째 값이 "name"컬럼에서 조회되거나 두 번째 값이 "age" 컬럼에서 조회되는 데이터 반환.
- findByAgeLessThan() - [ 쿼리 : ~ WHERE age<?1 ]
"age" 컬럼의 값 중 파라미터로 들어온 값보다 작은 데이터 반환.
- findByAgeGreaterThan() - [ 쿼리 : ~ WHERE age>?1 ]
"age" 컬럼의 값 중 파라미터로 들어온 값보다 큰 데이터 반환.
- findByName(Is)NULL() - [ 쿼리 : ~ WHERE name IS NULL ]
"name" 컬럼의 값 중 null인 데이터 반환.
// service - UserDetailService.java
@RequiredArgsConstructor
@Service
public class UserDetailService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public User loadUserByUsername(String email) {
return userRepository.findByEmail(email)
.orElseThrow(() -> new IllegalArgumentException((email)));
}
}
스프링 시큐리티에서 로그인을 진행할 때 사용자 정보를 가져오는 역할을 한다.
UserDetailsService 인터페이스
스프링 시큐리티에서 사용자의 정보를 가져오는 인터페이스이다. 사용자의 인증 정보를 제공하는 단일 메서드를 정의하고, 주로 사용자의 인증에 필요한 정보를 데이터베이스나 외부 저장소에서 가져오는 작업을 구현 한다.
loadUserByUsername() 메서드를 필수적으로 구현해야하므로 오버라이딩 하여 사용자 정보를 가져오는 로직을 작성한다.
userRepository.findByEmail(email)
주어진 이메일에 해당하는 사용자를 데이터베이스에서 찾고, Optional를 반환한다.
orElseThrow(() -> new IllegalArgumentException(email))
findByEmail 메서드가 빈 Optional을 반환할 경우 예외를 발생시킨다. 이 예외는 인자로 받은 이메일을 포함하여 IllegalArgumentException을 throw 한다.
일반적으로 메서드의 매개변수 유효성을 검사하고, 유효하지 않은 경우에 IllegalArgumentException을 throw하는 것이 좋다.
이 예외는 주로 메서드에 전달된 인수가 허용되지 않는 값이거나 유효하지 않은 경우에 발생한다.