인프런의 Spring Boot JWT Tutorial의 강의를 보고 정리해 보았습니다.
이전 파트에서 JWT 관련 설정법에 대해서 기록하였다.
[Spring Boot JWT Tutorial - 인프런] 스프링 JWT 적용하기 part2. JWT 관련 설정하기
1. DTO 생성하기
▶ LoginDto
- 로그인 시 사용
import lombok.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LoginDto {
@NotNull
@Size(min = 3, max = 50)
private String username;
@NotNull
@Size(min = 3, max = 100)
private String password;
}
▶ TokenDto
- Token 정보를 Response할 때 사용
import lombok.*;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TokenDto {
private String token;
}
▶ UserDto
- 회원가입시에 사용
import com.example.tutorial.entity.User;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Set;
import java.util.stream.Collectors;
@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;
private Set<AuthorityDto> authorityDtoSet;
public static UserDto from(User user) {
if(user == null)
return null;
return UserDto.builder()
.username(user.getUsername())
.nickname(user.getNickname())
.authorityDtoSet(user.getAuthorities().stream()
.map(authority -> AuthorityDto.builder().authorityName(authority.getAuthorityName()).build())
.collect(Collectors.toSet()))
.build();
}
}
2. UserRepository 인터페이스
import com.example.tutorial.entity.User;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
//User정보를 가져올 때 권한 정보도 같이 가져옴
@EntityGraph(attributePaths = "authorities") // 쿼리가 수행이 될때 Lazy 조회가 아니고 Eager조회롤 authorities 정보를 같이 가져오게 된다.
Optional<User> findOneWithAuthoritiesByUsername(String username);
}
▶ CustomDetailsService
- UserDetailsService를 구현한 클래스로 Spring Security에서 중요한 클래스이다.
package com.example.tutorial.service;
import com.example.tutorial.entity.User;
import com.example.tutorial.repository.UserRepository;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Component("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
//로그인 시에 DB에서 유저 정보와 권한 정보를 가져오게 된다. 해당 정보를 기반으로 userDetailsUser객체를 생성후 리턴
@Override
@Transactional
public UserDetails loadUserByUsername(final String username) {
return userRepository.findOneWithAuthoritiesByUsername(username)
.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());
//활성화 되어 있다면 유저이름, 유저 비밀번호, 유저 권한정보를 기반으로 userDetails.User 객체 생성
return new org.springframework.security.core.userdetails.User(user.getUsername(),
user.getPassword(),
grantedAuthorities);
}
}
3. 로그인 API와 관련 로직
▶ AuthController
- 로그인 API
import com.example.tutorial.dto.LoginDto;
import com.example.tutorial.dto.TokenDto;
import com.example.tutorial.jwt.JwtFilter;
import com.example.tutorial.jwt.TokenProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("/api")
public class AuthController {
private final TokenProvider tokenProvider;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
public AuthController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
this.tokenProvider = tokenProvider;
this.authenticationManagerBuilder = authenticationManagerBuilder;
}
@PostMapping("/authenticate")
public ResponseEntity<TokenDto> authorize(@Valid @RequestBody LoginDto loginDto) {
//로그인 파라미터를 통해 authentication 토큰 객체 생성
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken); // authenticate(authenticationToken)메서드가 실행이 될 때 CustomUserDetailService의 loadUserByUsername메서드가 실행된다. ???
SecurityContextHolder.getContext().setAuthentication(authentication); // 위의 authentication을 SecurityContext에 저장
String jwt = tokenProvider.createToken(authentication); //위의 인증 정보를 기준으로 jwt 토큰 생성
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(JwtFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); //jwt 토큰을 Response 헤더에 넣어줌
return new ResponseEntity<>(new TokenDto(jwt), httpHeaders, HttpStatus.OK); //Response 바디에도 넣어줘서 리턴
}
}
그 외
Postman에서 토큰 정보를 받아 테스트하고 싶다면 Tests에 다음과 같이 넣어준다.
var jsonData = JSON.parse(responseBody)
pm.globals.set("jwt_tutorial_token", jsonData.token);
그리고 Request의 Auth의 Bearer Token으로 `{{jwt_tutorial_token}}`으로 토큰 정보를 받아온다.
다음 포스팅
[Spring Boot JWT Tutorial - 인프런] 스프링 JWT 적용하기 part4. 회원가입 API 구현 및 권한 검증 확인
'Spring' 카테고리의 다른 글
[Spring Batch] Batch 이해하기 (0) | 2023.03.07 |
---|---|
[Spring Boot JWT Tutorial - 인프런] 스프링 JWT 적용하기 part4. 회원가입 API 구현 및 권한 검증 확인 (0) | 2022.09.03 |
[Spring Boot JWT Tutorial - 인프런] 스프링 JWT 적용하기 part2. JWT 관련 설정하기 (0) | 2022.09.03 |
[Spring Boot JWT Tutorial - 인프런] 스프링 JWT 적용하기 part1. 초기 세팅 (0) | 2022.09.03 |
7. JDBC, SQL Mapper, ORM (2) | 2022.07.15 |