๐Ÿ“Œ Spring Security - "UserDetails" & "PrincipalDetails" ์™„์ „ ์ •๋ณต

My Pale Blue Dotยท2025๋…„ 4์›” 30์ผ
0

SPRING

๋ชฉ๋ก ๋ณด๊ธฐ
33/36
post-thumbnail

๐Ÿ“… ๋‚ ์งœ

2025-04-30


๐Ÿ“ ํ•™์Šต ๋‚ด์šฉ

1๏ธโƒฃ Spring Security ์ธ์ฆ ๊ตฌ์กฐ ๊ฐœ์š”

Spring Security์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ธ์ฆ ์ ˆ์ฐจ๊ฐ€ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค:

  1. UsernamePasswordAuthenticationFilter๊ฐ€ ๋กœ๊ทธ์ธ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ”
  2. loadUserByUsername() ํ˜ธ์ถœ
  3. ๋ฐ˜ํ™˜๋œ UserDetails ๊ฐ์ฒด๋ฅผ ๋‚ด๋ถ€์—์„œ ๊ฒ€์ฆ ๋ฐ Authentication ์ƒ์„ฑ
  4. ์ธ์ฆ๋œ ์ •๋ณด๋Š” SecurityContextHolder์— ์ €์žฅ๋จ

์ด๋ฅผ ์œ„ํ•ด ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•  ๋‘ ๊ฐ€์ง€ ํ•ต์‹ฌ ํด๋ž˜์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

  • โœ… PrincipalDetails: UserDetails ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„์ฒด
  • โœ… PrincipalDetailsService: UserDetailsService ๊ตฌํ˜„์ฒด

2๏ธโƒฃ PrincipalDetails ํด๋ž˜์Šค ๊ตฌํ˜„ (UserDetails ๊ตฌํ˜„์ฒด)

โœ… ์—ญํ• 

  • Spring Security๊ฐ€ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก 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;  // ๊ณ„์ • ํ™œ์„ฑํ™”
    }
}

3๏ธโƒฃ PrincipalDetailsService ํด๋ž˜์Šค ๊ตฌํ˜„ (UserDetailsService)

โœ… ์—ญํ• 

  • ๋กœ๊ทธ์ธ ์‹œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ID(username)๋กœ DB์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์กฐํšŒ
  • ์กฐํšŒ ๊ฒฐ๊ณผ๋ฅผ 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);
    }
}

4๏ธโƒฃ 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}


๐Ÿ”ฅ ์ •๋ฆฌ

๊ตฌ์„ฑ ์š”์†Œ์„ค๋ช…
PrincipalDetailsUserDetails ๊ตฌํ˜„์ฒด. UserDto๋ฅผ ๊ฐ์‹ธ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด๋กœ ๋ณ€ํ™˜
PrincipalDetailsServiceUserDetailsService ๊ตฌํ˜„์ฒด. ๋กœ๊ทธ์ธ ์‹œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ DB์—์„œ ์กฐํšŒ
UserMapper์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ DB์—์„œ ์กฐํšŒ/์ €์žฅํ•˜๋Š” MyBatis ์ธํ„ฐํŽ˜์ด์Šค
SecurityContextHolder์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๊ฐ€ ์ €์žฅ๋˜๋Š” ์ „์—ญ ๋ณด์•ˆ ์ปจํ…์ŠคํŠธ

๐Ÿ”— ์ฐธ๊ณ  ์ž๋ฃŒ


๋А๋‚€ ์ 

์ด๋ฒˆ ํ•™์Šต์„ ํ†ตํ•ด UserDetails์™€ UserDetailsService๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ๋ช…ํ™•ํžˆ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

ํŠนํžˆ, Spring Security ๋‚ด๋ถ€์—์„œ ์ธ์ฆ ๊ฐ์ฒด๋ฅผ ์–ด๋–ป๊ฒŒ ์ €์žฅํ•˜๊ณ  ๊บผ๋‚ด๋Š”์ง€, ์šฐ๋ฆฌ๊ฐ€ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•œ UserDto๋ฅผ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๋ž˜ํ•‘ํ•ด์•ผ ํ•˜๋Š”์ง€๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

์‹ค๋ฌด์—์„œ๋Š” ๊ถŒํ•œ์— ๋”ฐ๋ผ ํ™”๋ฉด์„ ๋‚˜๋ˆ„๊ฑฐ๋‚˜ ๊ธฐ๋Šฅ์„ ์ œํ•œํ•˜๋Š” ๋ฐ์— ์ด ๊ตฌ์กฐ๊ฐ€ ํ•ต์‹ฌ ์—ญํ• ์„ ํ•˜๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.


์š”์•ฝ

  • ๋กœ๊ทธ์ธ ์š”์ฒญ ์‹œ PrincipalDetailsService์˜ loadUserByUsername()์ด ํ˜ธ์ถœ๋จ
  • ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ DB์—์„œ ์กฐํšŒ๋˜์–ด PrincipalDetails์— ๋‹ด๊ฒจ ๋ฐ˜ํ™˜๋จ
  • PrincipalDetails๋Š” UserDetails ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„์ฒด๋กœ, Spring Security ๋‚ด๋ถ€์—์„œ ์ธ์ฆ ๊ฐ์ฒด๋กœ ์‚ฌ์šฉ๋จ
  • ๊ถŒํ•œ์€ SimpleGrantedAuthority๋กœ ๊ฐ์‹ธ์•ผ ํ•˜๋ฉฐ, ๋ฌธ์ž์—ด๋งŒ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์•ˆ ๋จ

profile
Here, My Pale Blue.๐ŸŒ

0๊ฐœ์˜ ๋Œ“๊ธ€