2025-04-30
Spring Security์์ ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ธ์ฆ ์ ์ฐจ๊ฐ ์งํ๋ฉ๋๋ค:
UsernamePasswordAuthenticationFilter
๊ฐ ๋ก๊ทธ์ธ ์์ฒญ์ ๊ฐ๋ก์ฑloadUserByUsername()
ํธ์ถUserDetails
๊ฐ์ฒด๋ฅผ ๋ด๋ถ์์ ๊ฒ์ฆ ๋ฐ Authentication
์์ฑSecurityContextHolder
์ ์ ์ฅ๋จ์ด๋ฅผ ์ํด ์ฐ๋ฆฌ๊ฐ ์ง์ ๊ตฌํํด์ผ ํ ๋ ๊ฐ์ง ํต์ฌ ํด๋์ค๊ฐ ์์ต๋๋ค:
PrincipalDetails
: UserDetails
์ธํฐํ์ด์ค ๊ตฌํ์ฒดPrincipalDetailsService
: UserDetailsService
๊ตฌํ์ฒดPrincipalDetails
ํด๋์ค ๊ตฌํ (UserDetails ๊ตฌํ์ฒด)UserDto
๋ฅผ ๊ฐ์ผ ๋ํผ ํด๋์คSecurityContext
์ ์ ์ฅ๋จ@Data
@NoArgsConstructor
@AllArgsConstructor
public class PrincipalDetails implements UserDetails {
private UserDto userDto; // ์ฐ๋ฆฌ๊ฐ ๋ง๋ UserDto ๊ฐ์ฒด
// ์ฌ์ฉ์์ ๊ถํ ๋ชฉ๋ก ๋ฐํ
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
// SimpleGrantedAuthority: ๊ถํ ๋ฌธ์์ด์ ๊ฐ์ฒด ํํ๋ก ๋ํ
authorities.add(new SimpleGrantedAuthority(userDto.getRole())); // ex) ROLE_USER
return authorities;
}
@Override
public String getPassword() {
return userDto.getPassword(); // ์ฌ์ฉ์ ๋น๋ฐ๋ฒํธ
}
@Override
public String getUsername() {
return userDto.getUsername(); // ์ฌ์ฉ์ ID (unique)
}
@Override
public boolean isAccountNonExpired() {
return true; // ๊ณ์ ๋ง๋ฃ ์ ๋จ
}
@Override
public boolean isAccountNonLocked() {
return true; // ๊ณ์ ์ ๊น ์์
}
@Override
public boolean isCredentialsNonExpired() {
return true; // ๋น๋ฐ๋ฒํธ ๋ง๋ฃ ์ ๋จ
}
@Override
public boolean isEnabled() {
return true; // ๊ณ์ ํ์ฑํ
}
}
PrincipalDetailsService
ํด๋์ค ๊ตฌํ (UserDetailsService
)PrincipalDetails
๋ก ๊ฐ์ธ ๋ฐํUsernameNotFoundException
๋ฐ์@Service
@Slf4j
public class PrincipalDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// ๋ก๊ทธ์ธ ์ ์ฌ์ฉ์๊ฐ ์
๋ ฅํ username ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก DB ์กฐํ
UserDto userDto = userMapper.selectAt(username);
// ์กฐํ ์คํจ ์ ์์ธ ๋ฐ์
if (userDto == null) {
throw new UsernameNotFoundException(username + " ์กด์ฌํ์ง ์๋ ๊ณ์ ์
๋๋ค.");
}
// ์กฐํ๋ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ PrincipalDetails ๊ฐ์ฒด์ ๋ด์ ๋ฐํ
return new PrincipalDetails(userDto);
}
}
UserMapper
์ธํฐํ์ด์ค (MyBatis ๊ธฐ๋ฐ)@Select
, @Insert
์ด๋
ธํ
์ด์
์ผ๋ก SQL ์ง์ ๋ช
์@Mapper
public interface UserMapper {
@Insert("INSERT INTO tbl_user (username, password, role) VALUES (#{username}, #{password}, #{role})")
int insert(UserDto userDto);
@Select("SELECT * FROM tbl_user WHERE username = #{username}")
UserDto selectAt(String username);
}
โ @Select ์ฟผ๋ฆฌ์์ ๊ดํธ ์ค๋ฅ ์ฃผ์: username=#{username}) โ username=#{username}
๊ตฌ์ฑ ์์ | ์ค๋ช |
---|---|
PrincipalDetails | UserDetails ๊ตฌํ์ฒด. UserDto ๋ฅผ ๊ฐ์ธ ์ธ์ฆ๋ ์ฌ์ฉ์ ์ ๋ณด๋ก ๋ณํ |
PrincipalDetailsService | UserDetailsService ๊ตฌํ์ฒด. ๋ก๊ทธ์ธ ์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ DB์์ ์กฐํ |
UserMapper | ์ฌ์ฉ์ ์ ๋ณด๋ฅผ DB์์ ์กฐํ/์ ์ฅํ๋ MyBatis ์ธํฐํ์ด์ค |
SecurityContextHolder | ์ธ์ฆ๋ ์ฌ์ฉ์๊ฐ ์ ์ฅ๋๋ ์ ์ญ ๋ณด์ ์ปจํ ์คํธ |
์ด๋ฒ ํ์ต์ ํตํด UserDetails
์ UserDetailsService
๊ฐ ์ด๋ป๊ฒ ๋์ํ๋์ง ๋ช
ํํ ์ดํดํ ์ ์์๋ค.
ํนํ, Spring Security ๋ด๋ถ์์ ์ธ์ฆ ๊ฐ์ฒด๋ฅผ ์ด๋ป๊ฒ ์ ์ฅํ๊ณ ๊บผ๋ด๋์ง, ์ฐ๋ฆฌ๊ฐ ์ปค์คํฐ๋ง์ด์งํ UserDto
๋ฅผ ์ด๋ค ๋ฐฉ์์ผ๋ก ๋ํํด์ผ ํ๋์ง๋ฅผ ์ง์ ๊ตฌํํ๋ฉด์ ์ฒด๊ฐํ ์ ์์๋ค.
์ค๋ฌด์์๋ ๊ถํ์ ๋ฐ๋ผ ํ๋ฉด์ ๋๋๊ฑฐ๋ ๊ธฐ๋ฅ์ ์ ํํ๋ ๋ฐ์ ์ด ๊ตฌ์กฐ๊ฐ ํต์ฌ ์ญํ ์ ํ๊ฒ ๋ ๊ฒ์ด๋ค.
PrincipalDetailsService
์ loadUserByUsername()
์ด ํธ์ถ๋จPrincipalDetails
์ ๋ด๊ฒจ ๋ฐํ๋จPrincipalDetails
๋ UserDetails
์ธํฐํ์ด์ค ๊ตฌํ์ฒด๋ก, Spring Security ๋ด๋ถ์์ ์ธ์ฆ ๊ฐ์ฒด๋ก ์ฌ์ฉ๋จSimpleGrantedAuthority
๋ก ๊ฐ์ธ์ผ ํ๋ฉฐ, ๋ฌธ์์ด๋ง ๋ฐํํ๋ฉด ์ ๋จ