지금 부터 혼자서 하는 토이 프로젝트를 해볼려고 한다.
쇼핑몰을 만들어 볼 생각이다.
첫번째 순서는 JWT를 이용해서 회원 가입을 하는 것이다.
JWT란?
지금부터 JWT에 대한 내용은 JWT 이곳을 참고했다.
JWT는 Json Web Token의 약자로 인증에 필요한 정보들을 암호화시킨 JSON 토큰이다.
JWT 기반 인증은 JWT 토큰(Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별하는 방식이다.
JWT는 JSON 데이터를 Base64 URL-safe Encode 를 통해 인코딩하여 직렬화한 것이며, 토큰 내부에는 위변조 방지를 위해 개인키를 통한 전자서명도 들어있다.
JWT는 Header, Payload, Signature 3개로 이루어져 있다.
Header
- alg : 서명 암호화 알고리즘 (ex. HS256)
- typ : 토큰 유형 (JWT)
Payload
토큰에서 사용할 정보의 조각들인 Claim이 담겨있다. 실제로 사용될 정보에 대한 내용을 담고 있다.
나는 JWT를 만들 때, password를 담았다.
Key - value 형식 = Map 형식으로 한 쌍의 정보를 Claim으로 저장하고 있다.
- Registed claims : 미리 정의된 클레임 (issuer : 발행자, expireation time : 만료 시간, subject : 제목, issued At : 발행 시간, jti : JWI ID) 등이 담겨 있다.
- Public claims : 사용자가 정의할 수 있는 클레임 공개용 정보 전달을 위해 사용한다.
- Private claims : 해당하는 당사자들 간에 정보를 공유하기 위해 만들어진 사용자 지정 클레임, 외부에 공개되도 상관 없지만 해당 유저를 특정할 수 있는 정보를 담고 있다.
Signature
시그니처에서 사용하는 알고리즘은 헤더에서 정의한 알고리즘 방식을 활용한다.
시그니처의 구조는 해더와 페이로드와 서버가 갖고 있는 유일한 Key 값을 합친 것을 헤더에서 정의한 알고리즘으로 암호화를 한다.
Member Entity
나는 이 프로젝트를 Spring Boot와 JPA를 이용할 것 이다.
@Entity
@Getter
@Table(name = "member")
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private Integer id;
@Column(unique = true, nullable = false)
private String email;
@Column(unique = true, nullable = false)
private String phoneNumber;
@Column(unique = true, nullable = false)
private String name;
private String password;
private boolean status = true;
private LocalDateTime createAt = LocalDateTime.now();
private LocalDateTime updateAt = LocalDateTime.now();
//== 생성 메소드 ==//
public void createMember(MemberPostReq memberPostReq){
this.email = memberPostReq.getEmail();
this.phoneNumber = memberPostReq.getPhoneNumber();
this.name = memberPostReq.getName();
}
public void createPassword(String password){
this.password = password;
}
}
Member 엔티티이다.
이 프로젝트에는 email, phoneNumber, name,. password 등을 받아 볼려고한다. password는 받아서 JWT 처리를 해준 뒤 JWT를 DB에 저장할 것이다. Member의 내용은 프로젝트를 진행하면서 계속 변경될 것 같다.
JwtService
JWT를 만들고 받고 decode하는 메소드들이 존재하는 곳이다.
private Key key = Keys.hmacShaKeyFor(Encoders.BASE64.encode(Secret.JWT_SECRET_KEY.getBytes()).getBytes());
JWT를 하기전 key 부터 만들어 주었다. JWT_SECRET_KEY를 BASE64를 이용해 Encode 한 것이다.
JWT_SECRET_KEY를 만드는 방법은 여러가지가 있지만 나는 cmd 창에서 openssl rand -hex 32로 만들었다.
- 헤더에서 JWT 받아오기
/**
* Header에서 Authorization 으로 JWT 호출
*/
public String getJwt(){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
return request.getHeader("Authorization");
}
Header의 Authorization에 JWT를 입력하면 그대로 받아오는 메소드 이다.
- JWT 생성
/**
* JWT 생성
*/
public String createJwt(String password){
Date now = new Date();
return Jwts.builder()
.setHeaderParam("type","jwt") // type은 jwt
.claim("password",password) // Payload에 담길 데이터 입력
.setIssuer("Woong") // 토큰발급자
.setIssuedAt(now) // 발급 시간
.setExpiration(new Date(System.currentTimeMillis()+1*(1000*60*60*24*365))) // JWT 만료시간 설정, 1년으로 설정
.signWith(key, SignatureAlgorithm.HS256) // 서명 알고리즘과 비밀 키를 받는다.
.compact(); //JWT 토큰을 생성한다
}
MemberRepository
public interface MemberRepository extends JpaRepository<Member, Integer> {
@Query("select m from Member m where m.email = :email")
Member findEmail(@Param("email") String email);
}
이메일 중복이 있는지 확인하는 Query이다.
MemberService
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
private final JwtService jwtService;
/**
* Member 생성(JWT 이용)
*/
public String createMember(MemberPostReq memberPostReq) throws BaseException {
if(validateEmail(memberPostReq.getEmail()) == false){
throw new BaseException(BaseResponseStatus.POST_USERS_EXISTS_EMAIL);
}
Member member = new Member();
member.createMember(memberPostReq);
member.createPassword(jwtService.createJwt(memberPostReq.getPassword())); //JWT로 변경
memberRepository.save(member);
return "회원가입을 완료했습니다!";
}
public boolean validateEmail(String email){ //이메일이 중복인지 검사
Member emailCheck = memberRepository.findEmail(email);
if(emailCheck == null) return true;
else return false;
}
}
회원가입을 하는 메소드이다.
MemberController
- MemberPostReq
@Data
public class MemberPostReq {
private String email;
private String phoneNumber;
private String name;
private String password;
}
@RestController //controller와 RestController의 차이?
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
@PostMapping("/user/create")
public BaseResponse<String> createUser(@RequestBody MemberPostReq memberPostReq){
try{
return new BaseResponse<>(memberService.createMember(memberPostReq));
} catch(BaseException baseException){
return new BaseResponse<>(baseException.getStatus());
}
}
}
이 Project를 하면 Controler와 RestController의 차이가 궁금해졌다.
옛날에 배운거 같지만 까먹어서 다시한번 찾아봤다.
@Controller는 View를 반환하기 위해 사용한다. 클라이언트가 요청을 하면 Controller가 받아 Service 및 Repository가 Database와 소통을 하고 대답을 View의 형태로 클라이언트에 돌려준다. 하지만 Controller가 Data를 Client에게 전해줘야 할 때도 있다. 이때 @ResponseBody를 사용해 Json 형태로 Data를 반환하면 된다.
@RestController 는 @Controller에 @ResponseBody를 추가한 것이다. 즉, 그냥 Json 형태로 객체 데이터를 반환한다. 최근에 데이터를 응답으로 제공하는 REST API를 개발할 때 주로 사용하며 객체를 ResponseEntity로 감싸서 반환합니다.
실행
이제 포스트맨으로 직접 실행을 해봐야겠다. 이 프로젝트는 간단하게 Database와 연결하기 위해 H2Database를 사용했다.
JWT로 잘 변경되어서 DB에 들어갔다.
다음에는 JWT로 암호화 했던 Paswword를 다시 원상태 값으로 뽑아오는 것을 해봐야겠다.
'토이프로젝트 > 나만의 프로젝트' 카테고리의 다른 글
Docker로 CI/CD 구축하기 (0) | 2023.06.20 |
---|---|
데이터베이스 정규화 하기 (2) | 2023.04.29 |
OAuth2.0 이란? (2) | 2023.02.07 |
SpringBoot를 이용한 이메일 인증 하기 (0) | 2023.02.06 |
Spring Boot와 JWT를 이용한 회원 가입(2) (0) | 2023.02.05 |