Spring Security UserDetails, UserDetailsService 란? - 삽질중인 개발자
Spring Security - UserDetails , UserDetailsService
UserDetails 란?
Spring Security에서 사용자의 정보를 담는 인터페이스이다.
Spring Security에서 사용자의 정보를 불러오기 위해서 구현해야 하는 인터페이스로 기본 오버라이드 메서드들은 아래와 같다.
메소드 | 리턴 타입 | 설명 | 기본값 |
getAuthorities() | Collection<? extends GrantedAuthority> | 계정의 권한 목록을 리턴 | |
getPassword() | String | 계정의 비밀번호를 리턴 | |
getUsername() | String | 계정의 고유한 값을 리턴 ( ex : DB PK값, 중복이 없는 이메일 값 ) |
|
isAccountNonExpired() | boolean | 계정의 만료 여부 리턴 | true ( 만료 안됨 ) |
isAccountNonLocked() | boolean | 계정의 잠김 여부 리턴 | true ( 잠기지 않음 ) |
isCredentialsNonExpired() | boolean | 비밀번호 만료 여부 리턴 | true ( 만료 안됨 ) |
isEnabled() | boolean | 계정의 활성화 여부 리턴 | true ( 활성화 됨 ) |
여기서 잘 봐야하는 메서드가 getUsername()이다.
username은 계정의 고유한 값인데 다른 블로그들을 보니까 다들 email( 로그인용 아이디 )을 넘겨준다고 하지만 email( 로그인용 아이디 )은 SSO 같은 서버를 만들게 되면 정책에 따라서 중복이 될 수도 있기에 나와 같은 경우는 보통 DB에서 User Table에 PK 값을 넘겨준다.
CustomUserDetails 구현하기
대부분의 경우 Spring Security의 기본 UserDetails로는 실무에서 필요한 정보를 모두 담을 수 없기에 아래와 같은 CustomUserDetails를 구현하여 사용한다.
@Getter
public class CustomUserDetails implements UserDetails, Serializable {
private static final long serialVersionUID = 174726374856727L;
private String id; // DB에서 PK 값
private String loginId; // 로그인용 ID 값
private String password; // 비밀번호
private String email; //이메일
private boolean emailVerified; //이메일 인증 여부
private boolean locked; //계정 잠김 여부
private String nickname; //닉네임
private Collection<GrantedAuthority> authorities; //권한 목록
/**
* 해당 유저의 권한 목록
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
/**
* 비밀번호
*/
@Override
public String getPassword() {
return password;
}
/**
* PK값
*/
@Override
public String getUsername() {
return id;
}
/**
* 계정 만료 여부
* true : 만료 안됨
* false : 만료
* @return
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 계정 잠김 여부
* true : 잠기지 않음
* false : 잠김
* @return
*/
@Override
public boolean isAccountNonLocked() {
return locked;
}
/**
* 비밀번호 만료 여부
* true : 만료 안됨
* false : 만료
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 사용자 활성화 여부
* ture : 활성화
* false : 비활성화
* @return
*/
@Override
public boolean isEnabled() {
//이메일이 인증되어 있고 계정이 잠겨있지 않으면 true
return (emailVerified && !locked);
}
}
UserDetailsService 란?
Spring Security에서 유저의 정보를 가져오는 인터페이스이다.
Spring Security에서 유저의 정보를 불러오기 위해서 구현해야하는 인터페이스로 기본 오버라이드 메서드는 아래와 같다.
메소드 | 리턴 타입 | 설명 |
loadUserByUsername | UserDetails | 유저의 정보를 불러와서 UserDetails로 리턴 |
UserDetailsServiceImpl 구현하기
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserInfoRepository userInfoRepository;
@Override
public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException {
UserInfo userInfo = userInfoRepository.findByLoginId(loginId).orElseThrow(() -> new UsernameNotFoundException("not found loginId : " + loginId));
CustomUserDetails customUserDetails = new CustomUserDetails();
// 값 세팅 로직 시작
...
// 값 세팅 로직 끝
return customUserDetails;
}
}
아마 대부분의 프로젝트에서 로그인 화면에서는 아래와 같은 HTML로 login용 ID와 Password를 함께 넘길 것이다.
<form action="/login" method="post">
<input type="text" id="loginId" name="loginId" autofocus="autofocus" /> <br />
<input type="password" id="password" name="password" /> <br />
<input type="submit" value="Log in" />
</form>
아래에 있는 시큐리티 설정을 해준다면
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.usernameParameter("loginId")
.passwordParameter("password")
}
}
Spring Security에서 loginId를 넘겨줄 것이고 loadUserByUsername 에서 해당 값으로 DB를 조회 후 저장되어 있는 유저 정보를 가져온다.
그 후에 원하는 형태로 CustomUserDetails를 세팅해준 후 리턴해주면 Spring Security에서는 해당 유저의 정보를 조회할 때에는 CustomUserDetails에 세팅된 값으로 조회를 한 후 로직을 처리해준다.
SecurityContextHolder에서 UserDetails 불러오기
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDetails userDetails = (UserDetails)principal;
String username = userDetails.getUsername();
String password = userDetails.getPassword();
기본적으로 Spring Security의 principal 객체는 Object 형태로 UserDetails를 형변환 해야 한다.