Login 페이지(loginform.html)를 통해 아이디, 비밀번호를 POST 요청 시, 스프링 시큐리티 인증 절차를 거쳐 로그인 진행
DB(데이터베이스)에 저장된 회원 정보를 조회하여
비밀번호를 검증하고
서버 세션 저장소에 해당 아이디에 대한 session을 저장
이때, 로그인 회원 정보를 검증하기 위해서는UserDetailService , UserDetails 클래스구현 필요
구조도
로그인 회원 정보를 검증하기 위해서는 UserDetailService , UserDetails 클래스 구현 필요
해당 클래스는 인터페이스이므로, 이를 상속받아 구현하는 클래스 필요
DB(데이터베이스)의 회원 정보를 가져와, 로그인된 정보를 비교하여 검증 가능
UserDetailsService
1. 의미
- Spring Security에서 유저의 정보를 가져오는 인터페이스
- 사용자가 로그인을 할 때 기본 회원인지 인증 하고, 인증이 완료되면 로그인한 상태의 사용자의 세션을 유지 시켜주기 위한 인터페이스
- 인증에 성공할 시 ▷ 해당 회원에 대한 session에 대한 정보가 생성되고 ▷ Authentication 객체나 @AuthenticationPrincipal 애노테이션을 통해서 session에 들어있는 사용자 정보를 사용할 수 있음 ▷ 즉, 이 메서드가 정상적으로 완료되어 UserDetails 정보가 반환되면 인증에 성공한 것
- 기본 오버라이드 메서드
▷DB에서 유저 정보를 불러오는 중요한 메서드로, 로그인한 회원이 기본 회원인지 아닌지 인증하는 메서드
2. 코드 구현하기
UserDetailsService 인터페이스 상속받는 CustomUserDetailsService 클래스를 생성
package com.example.securitytest.entity;
import jakarta.persistence.*;
import lombok.Data;
@Entity // table과 연결 - Entity를 기반으로 테이블을 만들어주는 hibernate 설정
@Data
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // id가 자동으로 생성
private int id;
@Column(unique = true) // username이 중복되어 저장되지 않도록 설정
private String username;
private String password;
private String role; // 권한 저장: 일반인(USER), 관리자(ADMIN)
}
UserDetails
1. 의미
- Spring Security가 사용자 정보를 알 수 있도록 사용자에 대한 정보를 담는 인터페이스 - 이 인터페이스를 구현하면, Spring Security에서 구현한 클래스를 사용자 정보로 인식하고 인증 작업 진행 - 즉, UserDetails 인터페이스는 VO(DTO) 역할을 함
- 인터페이스 구현 시, 기본 오버라이드 메서드
2. 코드 구현하기
UserDetails 인터페이스를 상속받는 CustomUserDetails 클래스 생성
Spring Security의 기본 UserDetails로는 필요한 정보를 모두 담을 수 없기 때문에 CustomUserDetails를 구현하여 사용
userDetailService로부터 전달받은 데이터( return new CustomUserDetails(entity) )
package com.example.securitytest.dto;
import com.example.securitytest.entity.UserEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
@RequiredArgsConstructor
public class CustomUserDetails implements UserDetails { //dto와 같은 역할을 함
private final UserEntity userEntity;
@Override // 사용자의 특정 권한: Role값에 대해 반환
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collection = new ArrayList<>();
collection.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return userEntity.getRole();
}
});
return collection;
}
@Override
public String getPassword() {
return userEntity.getPassword();
}
@Override
public String getUsername() {
return userEntity.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true; // 만료되지 않음
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
MainController
SecurityContextHolder에서 UserDetails(사용자 정보) 불러오기
package com.example.securitytest.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.Collection;
import java.util.Iterator;
@Controller
public class MainController {
@GetMapping("/")
public String main(Model m) {
//SecurityContextHolder에서 UserDetails 불러오기
// session에 담겨있는 로그인한 사용자의 특정한 이름 값
String id = SecurityContextHolder.getContext().getAuthentication().getName();
// session에 담겨있는 로그인한 사용자의 특정한 role 값
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iter = authorities.iterator();
GrantedAuthority auth = iter.next();
String role = auth.getAuthority();
// view에서 보여줄 데이터 저장
m.addAttribute("id", id);
m.addAttribute("role", role);
return "main";
}
}