[Spring] Spring Security ์ธ์ฆ ์ ์ฐจ Interface | UserDetails์ UserDetailsService
๐ UserDetails & UserDetailsService๋?
๐ฝ ๊ฐ์
UserDetailsService Interface๋ Data Base์์ User ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ์ญํ ์ ํ๋ ๊ฒ์ด์์.
ํด๋น Interface์ Method์์ Data Base User ์ ๋ณด๋ฅผ ๊ฐ์ ธ์์ AuthenticationProvider Interface๋ก User ์ ๋ณด๋ฅผ ๋ฐํํ๋ฉด ๊ทธ ๊ณณ์์ ์ด์ฉ์๊ฐ ์ ๋ ฅํ ์ ๋ณด์ DB์ ์๋ ํ์ ์ ๋ณด๋ฅผ ๋น๊ตํ๊ฒ ๋๋ ๊ฒ์ด์์. DB์์ ํ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ์์ ์ ํ์ํ Interface๋ UserDetails์ UserDetilasService Interface์ธ ๊ฒ์ด์์.
๐ฝ UserDetails
Spring Security์์ ์ด์ฉ์์ ์ ๋ณด๋ฅผ ๋ด๋ Interface๋ UserDetails Interface์ธ ๊ฒ์ด์์. ์ด Interface๋ฅผ ๊ตฌํํ๊ฒ ๋๋ฉด Spring Security์์ ๊ตฌํํ Class๋ฅผ ํตํด ์ด์ฉ์ ์ ๋ณด๋ฅผ ์ธ์ํ๊ณ , ์ธ์ฆ ์์ ์ ํ ์ ์๋ ๊ฒ์ด์์.
UserDetails Interface๋ VO(Value Object)์ ์ญํ ์ ํ๋ค๊ณ ๋ณด๋ฉด ๋๊ณ , ์ด๋ฅผ ํตํด ์ด์ฉ์ ์ ๋ณด๋ฅผ ๋ชจ๋ ๋ด์๋๋ ๊ฐ์ฒด๋ก ๊ตฌํํ ์ ์๋ ๊ฒ์ด์์.
UserDetails Interface๋ฅผ ๊ตฌํํ๊ฒ ๋๋ฉด Override ๋๋ Method๋ค์ด ์๋ ๊ฒ์ด์์. ์ด Method๋ค์ ๋จผ์ ํ์ ํ๋ ๊ฒ ์๋ฌด๋๋ ์ข์ ๊ฑฐ ๊ฐ์์.
๋ํ, ํ์ ์ ๋ณด์ ๊ดํ ๋ค๋ฅธ ์ ๋ณด(์ด๋ฆ, ๋์ด, ์๋ ์์ผ ๋ฑ๋ฑ..)๋ ์ถ๊ฐํ ์ ์๋ ๊ฒ์ด์์.
Override๋ Method๋ค๋ง Spring Security์์ ์์์ ์ด์ฉํ๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก Class๋ฅผ ๋ง๋ค์ง ์๊ณ , Member ๋ณ์๋ฅผ ์ถ๊ฐํด์ ๊ฐ์ด ์ฌ์ฉํด๋ ์ข์ ๊ฒ์ด์์.
๊ทธ๋ฆฌ๊ณ , MyBatis๋ฅผ ์ด์ฉํ๊ฒ ๋๋ค๋ฉด ์ฌ๊ธฐ์ Member ๋ณ์๋ค์ ๋ง๋ getter, setter๋ฅผ ๋ง๋ค์ด ์ฃผ๋ฉด ๋๋ ๊ฒ์ด์์.
Method | Return Type | Detail |
getAuthorities() | Collections<? extends GrantedAuthority> | ์ด์ฉ์ ๊ณ์ ์ด ๊ฐ๊ณ ์๋ ๊ถํ ๋ชฉ๋ก ๋ฐํ |
getPassword() | String | ์ด์ฉ์ ๊ณ์ ์ ๋น๋ฐ๋ฒํธ ๋ฐํ |
getUsername() | String | ์ด์ฉ์ ๊ณ์ ์ ์ด๋ฆ(ID)๋ฅผ ๋ฐํ |
isAccountNonExpired() | boolean | ์ด์ฉ์ ๊ณ์ ๋ง๋ฃ ์ฌ๋ถ ๋ฐํ true : ๋ง๋ฃ ๋์ง ์์ false : ๋ง๋ฃ |
isAccountNonLocked() | boolean | ์ด์ฉ์ ๊ณ์ ์ ๊ธ ์ฌ๋ถ ๋ฐํ true : ์ ๊ธฐ์ง ์์ false : ์ ๊น |
isCredentialNonExpired() | boolean | ๋น๋ฐ๋ฒํธ ๋ง๋ฃ ์ฌ๋ถ ๋ฐํ true : ๋ง๋ฃ ๋์ง ์์ false : ๋ง๋ฃ |
isEnable() | boolean | ๊ณ์ ํ์ฑํ(์ฌ์ฉ ๊ฐ๋ฅ) ์ฌ๋ถ ๋ฐํ true : ํ์ฑํ (์ด์ฉ ๊ฐ๋ฅ) false : ๋น ํ์ฑํ (์ด์ฉ ๋ถ๊ฐ) |
`getUsername()`์ ๊ณ์ ์ ์ด๋ฆ(ID)๋ฅผ ๋ฐํํ๋๋ฐ, ๊ทธ ๋ง์ธ ์ฆ, ์ฐ๋ฆฌ๊ฐ Login ํ ๋ ์ด์ฉํ๋ ID ํน์ E-mail์ ๋ฐํํ๋ ๊ฒ์ด์์. ๊ทธ๋ฆฌ๊ณ , ๊ณ์ ์ด ๋ง๋ฃ ๋์๋์ง ํน์ ๊ณ์ ์ด ์ ๊ฒจ ์๋์ง ๋ฑ์ ๋ํด ํ์ธํ ํ์๊ฐ ์๋ค๋ฉด ๋ฌด์กฐ๊ฑด true๋ฅผ ๋ฐํํ๊ฒ ํด ์ฃผ๋ฉด ๋๋ ๊ฒ์ด์์. ๋ง์ฝ ํ์ธํ Member ๋ณ์๊ฐ ์๋ค๋ฉด ๊ทธ Member ๋ณ์๋ฅผ ๋ฐํํด ์ฃผ๋ฉด ๋๋ ๊ฒ์ด์์.
์๋ ์์๋ ๊ณ์ ์ ํ์ฑ / ๋นํ์ฑํ๋ฅผ ํ์ธํ๋ Member ๋ณ์๊ฐ ์กด์ฌํ๊ณ , ์ด ๋๋ฌธ์ `isEnable()`์ ํด๋น Member ๋ณ์๋ฅผ ๋ฐํ ํด ์ฃผ๊ฒ ํ ๊ฒ์ด์์.
์์ ์ฝ๋
@SuppressWarnings("serial")
public class CustomUserDetails implements UserDetails {
private String id;
private String password;
private String authority;
private boolean enabled;
private String name;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<GrantedAuthority> auth = new ArrayList<GrantedAuthority>();
auth.add(new SimpleGrantedAuthority(AUTHORITY));
return auth;
} // getAuthorities() ๋
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return id;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
public String getNAME() {
return NAME;
}
public void setNAME(String name) {
this.name = name;
}
} // class ๋
์์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์ด์ฉ์ ์ ๋ณด๋ค์ Member ๋ณ์๋ฅผ ์ ์ธํด ์ค ์ ์๋ ๊ฒ์ด์์. ๋ ๋ง์ ์ ๋ณด๊ฐ ํ์ํ๋ค๋ฉด ์ถ๊ฐ๋ฅผ ํด๋ ๋ฉ๋๋ค.
์ ๊ทธ๋ฆผ์ Method๋ ๊ณ์ ์ด ๊ฐ๊ณ ์๋ ๊ถํ ๋ชฉ๋ก์ ๋ฐํํ๋ ์น๊ตฌ์ธ ๊ฒ์ด์์.
์์ `SimpleGrantedAutority()`๋ `GrantedAuthority`๋ฅผ Implementํ Class์ธ๋ฐ, `basic concrete implementations of a GrantedAuthority stores a String representation of an authority granted to the authentication object` ๊ถํ์ `String`์ผ๋ก ๋ฐํํด ์ฃผ๋ ์น๊ตฌ์ธ ๊ฒ์ด์์.
๊ฒฐ๊ตญ ๊ถํ ๋ชฉ๋ก์ ๋ฌธ์์ด๋ก ์ป๊ธฐ ์ํด ์์ Method๋ฅผ ์ด์ฉํ๋ ๊ฒ์ด์์.
์ด์ฉ์ ๊ณ์ ์ ํ์ฑ / ๋นํ์ฑํ ์ฌ๋ถ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด Member ๋ณ์๋ก ์ ์ธํ `enabled`๋ฅผ ๋ฐํํ๋ ๊ฒ์ด์์.
๐ฝ UserDetailsService
๐ฆ DevInquryReplyVO.java
์ด์ฉ์ ์ ๋ณด๋ฅผ ๋ด์ ๊ฐ์ฒด๋ฅผ `UserDetails`๋ฅผ ๊ตฌํํด์ ๋ง๋ค์๋ค๋ฉด Data Base์์ ์ด์ฉ์ ์ ๋ณด๋ฅผ ์ง์ ๊ฐ์ ธ์ค๋ Interface ๋ํ ๊ตฌํ ํด ์ฃผ์ด์ผ ํ๋ ๊ฒ์ด์์.
`UserDetailsService` Interface๋ JPA๋, MyBatis๋ฅผ ๊ตฌํํ Class ๋ฑ์ ํตํด Data Base์์ ์ด์ฉ์ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๋ ์์ฃผ ์ค์ํ ์ญํ ์ ํ๋ ์น๊ตฌ๊ฐ ์กด์ฌํ๋๋ฐ, ๊ทธ ์น๊ตฌ๊ฐ ๋ฐ๋ก `loadUserByUsername()`์ธ ๊ฒ์ด์์. ์ด ์น๊ตฌ๋ ์ด์ฉ์ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๋ ์์ ์ ํ๋ฉด ๋๋ ๊ฒ์ด์์.
`UserDetailsService` Interface๋ฅผ ๊ตฌํํ๋ฉด `loadUserByUsername()`๋ฅผ Overrideํด์ ์ด์ฉํด์ผ ํ๋ ๊ฒ์ด์์. ์ฌ๊ธฐ์์ CustomUserDetails ํ์ผ๋ก ์ด์ฉ์์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ฉด ๋๋ ๊ฒ์ด์์. ๊ฐ์ ธ์จ ์ด์ฉ์ ์ ๋ณด๊ฐ ์๋ค? ์๋ค?์ ๋ฐ๋ผ ์์ธ์ ์ด์ฉ์ ์ ๋ณด๋ฅผ ๋ฐํํด์ฃผ๋ฉด ๋๋ ๊ฒ์ด์์.
์์ ์ฝ๋
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserAuthDAO userAuthDAO;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
CustomUserDetails user = userAuthDAO.getUserById(username);
if(user==null) {
throw new UsernameNotFoundException(username);
}
return user;
} // loadUserByUsername(String username) ๋
} // class ๋
์์ Code์์ `loadUserByUsername()` Body๋ฅผ ๋ณด๋ฉด DAO์ `getUserById()`๋ฅผ ํธ์ถํ๋๋ฐ, ๋งค๊ฐ๋ณ์๋ก ๋ค์ด์จ username(์ด์ฉ์ ID)๋ฅผ ๋ฃ์ด์ฃผ์ด DB์์ ๊ฒ์์ ํ ๋ค ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์์์ `UserDatils`๋ฅผ ๊ตฌํํ ๊ตฌํ์ฒด `CustomUserDetails` ๊ฐ์ฒด ์๋ฃํ user ๋ณ์๋ก ๋ฐ์ ์ฃผ๋ ๊ฒ์ด์์.
๋ง์ฝ user ์ ๋ณด๋ฅผ ๋ชป ์ฐพ์๋ค๋ฉด Login์ ํ ์ ์๋ ๊ฒ์ด๊ณ , ๊ทธ๋ ๋ค๋ฉด `UsernameNotFoundException()`์ด ํฐ์ง๋๋ก ๋์ด์๊ณ , ์กด์ฌํ๋ค๋ฉด ํ์ ์ ๋ณด๋ฅผ ๋ฐํ ํด ์ฃผ๋ฉด ๋๋ ๊ฒ์ด์์.
์ฐธ๊ณ Site : https://writerroom.tistory.com/211