최신글
hyeonga_code
PreProject_9_JWT 서비스 구현하기(TestCode 작성) 본문
반응형
build.gradle
dependencies {
// JWT
implementation "io.jsonwebtoken:jjwt:0.9.1"
// XML 문서와 자바 객체간 매핑 자동화
implementation 'javax.xml.bind:jaxb-api:2.3.1'
// Spring Security for SimpleGrantedAuthority
implementation 'org.springframework.boot:spring-boot-starter-security:2.7.17'
}
+ build.gradle 전체 코드
더보기
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.5'
id 'io.spring.dependency-management' version '1.1.4'
id "io.freefair.lombok" version "8.6"
}
group = 'com.hyeongarl'
version = '1.0-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-web-services'
implementation 'org.springframework.boot:spring-boot-starter-validation'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// MySQL JPA
runtimeOnly 'com.mysql:mysql-connector-j'
// JWT
implementation "io.jsonwebtoken:jjwt:0.9.1"
// XML 문서와 자바 객체간 매핑 자동화
implementation 'javax.xml.bind:jaxb-api:2.3.1'
// Spring Security for SimpleGrantedAuthority
implementation 'org.springframework.boot:spring-boot-starter-security:2.7.17'
}
tasks.named('test') {
useJUnitPlatform()
}
application.yml
#토큰 제공자 추가
jwt:
issuer: ${JWT_ISSUER_NAME}
secret_key: ${JWT_SECRET_KEY}
값을 application.properties에 설정해두었다.
JWT 설정 파일
JwtProperties
package com.hyeongarl.config.jwt;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Getter
@Setter
@Component
@ConfigurationProperties("jwt") // Java 클래스에 프로퍼티 값을 가져와 사용
public class JwtProperties {
private String issuer;
private String secretKey;
}
application.yml에 지정한 값을 가지고 오는 것 같다.
TokenProvider
package com.hyeongarl.config.jwt;
import com.hyeongarl.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.Date;
import java.util.Set;
@RequiredArgsConstructor
@Service
public class TokenProvider {
private final JwtProperties jwtProperties;
public String generatedToken(User user, Duration expiredAt) {
Date now = new Date();
return makeToken(new Date(now.getTime() + expiredAt.toMillis()), user);
} //end generatedToken()
// JWT 토큰 생성 메소드
private String makeToken(Date expiry, User user) {
Date now = new Date();
return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE) // 헤더 typ: JWT
.setIssuer(jwtProperties.getIssuer()) // properties에서 설정한 issuer
.setIssuedAt(now) // iat : 현재 시간
.setExpiration(expiry) // exp : expiry 값
.setSubject(user.getUserEmail()) // sub : 사용자 이메일
.claim("id", user.getUserId()) // 클레임 id : 사용자 id
// 서명 : 비밀값, 해시값을 HS256방식으로 암호화
.signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
.compact();
} //end makeToken()
// JWT 토큰 유효성 검증 메소드
public boolean validatedToken(String token) {
try {
Jwts.parser()
.setSigningKey(jwtProperties.getSecretKey()) // 비밀값으로 복호화
.parseClaimsJws(token);
return true;
} catch (Exception e) { // 복호화 과정 중 에러 발싱 시 유효하지 않은 토큰
return false;
}
} //end validToken()
// 토큰 기반 인증 정보 가져오는 메소드
public Authentication getAuthentication(String token) {
Claims claims = getClaims(token);
Set<SimpleGrantedAuthority> authorities
= Set.of(new SimpleGrantedAuthority("ROLE_USER"));
return new UsernamePasswordAuthenticationToken(
new org.springframework.security.core.userdetails.User(
claims.getSubject(), "", authorities), token, authorities);
} //end getAuthentication()
// 토큰 기반 사용자 ID 가져오는 메소드
public Long getUserId(String token) {
Claims claims = getClaims(token);
return claims.get("id", Long.class);
} //end getUserId()
private Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(jwtProperties.getSecretKey())
.parseClaimsJws(token)
.getBody();
} //end getClaims()
}
User
package com.hyeongarl.entity;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Table(name="users")
@NoArgsConstructor
@Getter
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="user_id", updatable = false)
private Long userId;
@Column(name = "user_email", nullable = false, unique = true)
private String userEmail;
@Column(name = "user_password")
private String password;
@Builder
public User(String userEmail, String password) {
this.userEmail = userEmail;
this.password = password;
} //end User()
}
JWT Test Code 작성
application.properties
JWT_ISSUER_NAME=test@gmail.com
JWT_SECRET_KEY=testSecretKey
application.yml
#토큰 제공자 추가
jwt:
issuer: ${JWT_ISSUER_NAME}
secret_key: ${JWT_SECRET_KEY}
JwtFactory
package com.hyeongarl.config.jwt;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Builder;
import lombok.Getter;
import java.time.Duration;
import java.util.Date;
import java.util.Map;
import static java.util.Collections.emptyMap;
/**
* JWT 토큰 서비스를 테스트하는데 사용할 모킹용 객체
*/
@Getter
public class JwtFactory {
private String subject = "test@email.com";
private Date issuedAt = new Date();
private Date expiration = new Date(new Date().getTime() + Duration.ofDays(14).toMillis());
private Map<String, Object> claims = emptyMap();
@Builder
public JwtFactory(String subject, Date issuedAt, Date expiration, Map<String, Object> claims) {
this.subject = subject != null ? subject : this.subject;
this.issuedAt = issuedAt != null ? issuedAt : this.issuedAt;
this.expiration = expiration != null ? expiration : this.expiration;
this.claims = claims != null ? claims : this.claims;
}
public static JwtFactory withDefaultValues() {
return JwtFactory.builder().build();
}
public String createToken(JwtProperties jwtProperties) {
return Jwts.builder()
.setSubject(subject)
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setIssuer(jwtProperties.getIssuer())
.setIssuedAt(issuedAt)
.setExpiration(expiration)
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
.compact();
}
}
TokenProviderTest
package com.hyeongarl.config.jwt;
import com.hyeongarl.entity.User;
import com.hyeongarl.repository.UserRepository;
import io.jsonwebtoken.Jwts;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import java.time.Duration;
import java.util.Date;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
/**
* TokenProvider 클래스 테스트
*/
@SpringBootTest
public class TokenProviderTest {
@Autowired
private TokenProvider tokenProvider;
@Autowired
private UserRepository userRepository;
@Autowired
private JwtProperties jwtProperties;
@Test
@DisplayName("generateToken()")
void generateTokenTest() {
User testUser = userRepository.save(User.builder()
.userEmail("user@email.com")
.password("1234")
.build());
String token = tokenProvider.generatedToken(testUser, Duration.ofDays(14));
Long userId = Jwts.parser()
.setSigningKey(jwtProperties.getSecretKey())
.parseClaimsJws(token)
.getBody()
.get("id", Long.class);
assertThat(userId).isEqualTo(testUser.getUserId());
} //end generateTokenTest()
@Test
@DisplayName("validateToken()")
void validateToken_invalidTest() {
String token = JwtFactory.builder()
.expiration(new Date(new Date().getTime() - Duration.ofDays(7).toMillis()))
.build()
.createToken(jwtProperties);
boolean result = tokenProvider.validatedToken(token);
assertThat(result).isFalse();
} //end validateToken_invalidTest()
@Test
@DisplayName("validateToken()")
void validateToken_validTest() {
String token = JwtFactory.withDefaultValues().createToken(jwtProperties);
boolean result = tokenProvider.validatedToken(token);
assertThat(result).isTrue();
} //end validateToken_validTest()
@Test
@DisplayName("getAuthentication()")
void getAuthenticationTest() {
String userEmail = "user@email.com";
String token = JwtFactory.builder()
.subject(userEmail)
.build()
.createToken(jwtProperties);
Authentication authentication = tokenProvider.getAuthentication(token);
assertThat(((UserDetails) authentication.getPrincipal()).getUsername()).isEqualTo(userEmail);
} //end getAuthenticationTest()
@Test
@DisplayName("getUserId()")
void getUserIdTest() {
Long userId = 1L;
String token = JwtFactory.builder()
.claims(Map.of("id", userId))
.build()
.createToken(jwtProperties);
Long userIdByToken = tokenProvider.getUserId(token);
assertThat(userIdByToken).isEqualTo(userId);
} //end getUserIdTest()
}
테스트 실행시 모두 실행되는 것을 확인할 수 있다.
테스트 데이터베이스에 데이터가 추가된다.
반응형
'Project_HYEONGARL' 카테고리의 다른 글
Project_02_예외 처리하기 @ControllerAdvice, @RestControllerAdvice, @ExceptionHandler (0) | 2024.05.30 |
---|---|
Project_01_Spring Boot Gradle Multi Module Project 생성하기 (0) | 2024.05.28 |
PreProject_8_SpringBoot 프로젝트 MySQL 연결하기 (0) | 2024.05.26 |
PreProject_07_Controller Test (TestRestTemplate) (0) | 2024.05.21 |
PreProject_06_Controller (0) | 2024.05.13 |