@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserDetailsImpl implements UserDetails {
private String email;
private String password;
private UserRole role;
private LoginProvider loginProvider;
private String nickname;
private ProfileImage profileImage;
private String refreshToken;
@Builder
public UserDetailsImpl(String email, String password, UserRole role, LoginProvider loginProvider, String nickname, ProfileImage profileImage, String refreshToken) {
this.email = email;
this.password = password;
this.role = role;
this.loginProvider = loginProvider;
this.nickname = nickname;
this.profileImage = profileImage;
this.refreshToken = refreshToken;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> grantedAuthority = new ArrayList<>();
grantedAuthority.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return getRole().toString();
}
});
return grantedAuthority;
}
@Override
public String getUsername() {
return email;
}
@Override
public String getPassword(){ return password;}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
UserDetailsImpl은 Authentication을 담고있는 UserDetails 인터페이스를 상속하는 클래스이다.
SecurityContextHolder에 담을 Authentication 객체를 만들 때 사용할 예정이고 나는 위와 같이 정의 해주었다.
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
/**
* methodName : loadUserByUsername
* author : Jaeyeop Jung
* description : Context에 담기 위해 UserDetails를 implement한 UserDetailsImpl을 반환
*
* @param id
* @return id를 통해 찾은 UserDetailsImpl 객체
*/
@Override
@Transactional
public UserDetailsImpl loadUserByUsername(String id) {
User findUser = userRepository.findById(Long.parseLong(id))
.orElseThrow(() -> new IncorrectDeleteUserRequestException(""));
if(findUser != null){
return UserDetailsImpl.builder()
.email(findUser.getEmail())
.password(findUser.getPassword())
.role(findUser.getRole())
.loginProvider(findUser.getLoginProvider())
.nickname(findUser.getNickname())
.profileImage(findUser.getProfileImage())
.refreshToken(findUser.getRefreshToken())
.build();
}
return null;
}
}
자 그러면 UserDetailsServiceImpl을 보자.
우리는 토큰을 검증하고 정상적인 토큰이라면 SecurityContextHolder에 담을 Authentication 객체를 만들어야한다.
public Authentication getAuthentication(String token){
UserDetailsImpl userDetails = userDetailsService.loadUserByUsername(String.valueOf(this.findUserIdByJwt(token)));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
그러기 위해서 이전 글의 JwtTokenProvider.class에 정의했던 getAuthentication 메소드를 사용할 것이다.
이 메소드는 UserDetailsServiceImpl의 loadUserByUsername 메소드를 실행한다. 그러면 UserDetailsImpl을 만들어서 반환 해준다.
다시 getAuthentication 메소드에서는 UsernamePasswordAuthenticationToken라는 시큐리티에서 사용하는 AuthenticationToken을 만들어서 반환 해준다.
최종적으로 JwtAuthenticationFilter에서는 SecurityContextHolder에 해당 반환 값을 담음으로 권한처리를 할 수 있게 해준다.