외부와의 통신에 사용할 DTO 클래스 생성
- 로그인 시 사용할 LoginDto 클래스 생성
// main/java/me/gagyeong/tutorial/dto/LoginDto.java
@Getter // Lombok 어노테이션들
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LoginDto { // 로그인 시 사용할 클래스 생성
@NotNull
@Size(min = 3, max = 50) // @valid 관련 어노테이션 추가
private String username;
@NotNull
@Size(min = 3, max = 100) // @valid 관련 어노테이션 추가
private String password;
}
- 토큰 정보를 Response할 때 사용할 TokenDto 클래스 생성
// main/java/me/gagyeong/tutorial/dto/TokenDto.java
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TokenDto { // 토큰 정보를 Response할 때 사용할 클래스 생성
private String token; // 토큰 필드를 가짐
}
- 회원가입 시에 사용할 UserDto 클래스 생성
// main/java/me/gagyeong/tutorial/dto/UserDto.java
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserDto { // 회원가입 시에 사용할 클래스 생성
@NotNull
@Size(min = 3, max = 50)
private String username;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@NotNull
@Size(min = 3, max = 100)
private String password;
@NotNull
@Size(min = 3, max = 50)
private String nickname;
}
Repository 관련 코드 생성
- User 엔티티에 매핑되는 UserRepository 인터페이스 생성
// main/java/me/gagyeong/tutorial/respository/UserRepository.java
// JpaRepository를 extends하여 findAll, save 등의 JPA 메소드를 기본적으로 사용할 수 있게 됨
public interface UserRepository extends JpaRepository<User, Long> { // User 엔티티에 매핑되는 인터페이스
// @EntityGraph 어노테이션은 쿼리가 수행될 때 Lazy 조회가 아닌 Eager 조회로 authorities 정보를 같이 가져옴
@EntityGraph(attributePaths = "authorities")
// findOneWithAuthoritiesByUsername 메소드는 username을 기준으로 User 정보를 가져올 때
// 권한 정보도 같이 가져오게 됨
Optional<User> findOneWithAuthoritiesByUsername(String username);
}
- Spring Security에서 중요한 부분 중 하나인 UserDetailService를 구현한 CustomUserDetailsService 클래스 생성
// main/java/me/gagyeong/tutorial/service/CustomUserDetailsService.java
@Component("userDetailsService")
// UserDetailsService를 implements 하고
public class CustomUserDetailsService implements UserDetailsService { // Spring Security에서 중요한 부분 중 하나인 UserDetailService를 구현한 클래스
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) { // UserRepository를 주입 받음
this.userRepository = userRepository;
}
@Override
@Transactional
// loadUserByUsername 메소드를 Override해서
public UserDetails loadUserByUsername(final String username) {
return userRepository.findOneWithAuthoritiesByUsername(username) // 로그인 시에 DB에서 유저정보와 권한정보를 가져오게 됨
.map(user -> createUser(username, user))
.orElseThrow(() -> new UsernameNotFoundException(username + " -> 데이터베이스에서 찾을 수 없습니다."));
}
private org.springframework.security.core.userdetails.User createUser(String username, User user) {
if (!user.isActivated()) { // 해당 정보를 기준으로 유저가 활성화 상태라면
throw new RuntimeException(username + " -> 활성화되어 있지 않습니다.");
}
List<GrantedAuthority> grantedAuthorities = user.getAuthorities().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getAuthorityName()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(user.getUsername(), // 유저정보와 권한정보를 가지고 userdetails.User 객체를 생성해서 리턴
user.getPassword(),
grantedAuthorities);
}
}
로그인 API, 관련 로직 생성
- 로그인 API를 추가하기 위해 AuthController 클래스 생성
// main/java/me/gagyeong/tutorial/controller/AuthController.java
@RestController
@RequestMapping("/api")
public class AuthController { // 로그인 API를 추가하기 위한 클래스
private final TokenProvider tokenProvider;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
public AuthController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
this.tokenProvider = tokenProvider; // TokenProvider와
this.authenticationManagerBuilder = authenticationManagerBuilder; // AuthenticationManagerBuilder를 주입받음
}
@PostMapping("/authenticate") // 로그인 API 경로는 /api/authenticate 로 Post 요청을 받음
public ResponseEntity<TokenDto> authorize(@Valid @RequestBody LoginDto loginDto) {
// LoginDto의 username, password를 파라미터로 받고
// 이를 이용해 UsernamePasswordAuthenticationToken을 생성
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());
// authenticationToken을 이용해서 authenticate 메소드가 실행될 때
// CustomUserDetailsService에서 loadUserMyUsername 메소드가 실행되어 Authentication 객체를 생성
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
// 생성된 Authentication 객체를 SecurityContext에 저장하고
SecurityContextHolder.getContext().setAuthentication(authentication);
// Authentication 객체의 인증정보를 기준으로 하여 createToken 메소드를 통해 JWT 토큰을 생성
String jwt = tokenProvider.createToken(authentication);
// JWT 토큰을 Response Header에도 넣어주고
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(JwtFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
// TokenDto를 이용해서 Response Body에도 넣어서 리턴
return new ResponseEntity<>(new TokenDto(jwt), httpHeaders, HttpStatus.OK);
}
}
- 로그인 API를 Postman으로 테스트
username과 password의 admin 계정의 정보는 data.sql의 insert 문의 서버가 시작될 때 자동실행되어 DB에 저장된 상태
- 추가)
Tests 탭에서 responseBody 에 있는 내용을 파싱해서 포스트맨의 전역변수에 설정해서
다른 Request에서도 해당 변수에 있는 값을 끌어서 쓸 수 있게 됨
참고 영상
'Java-Spring > Spring Boot JWT Tutorial' 카테고리의 다른 글
Spring Boot JWT Tutorial - 목차 (0) | 2023.07.03 |
---|---|
[Spring Boot JWT Tutorial] 회원가입, 권한검증 (0) | 2022.01.23 |
[Spring Boot JWT Tutorial] JWT 코드, Security 설정 추가 (0) | 2022.01.23 |
[Spring Boot JWT Tutorial] Security 설정, Data 설정 (0) | 2022.01.22 |
[Spring Boot JWT Tutorial] JWT 소개, 프로젝트 생성 (0) | 2022.01.22 |