JWT
build.gradle
버전 안 적어도 된다고 분명 누가 그랬었는데.... 안 적으니까 에러가 나서 적어줬다.
dependencies {
// ...
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}
application.properties
jwt 관련 환경변수 정의하기
#jwt
spring.jwt.accessTokenExpireTime=3600000
spring.jwt.refreshTokenExpireTime=1296000000
spring.jwt.secret=bGljcnVpdC1iYWNrZW5kLWphdmEtdGhpc2lzc2VjcmV0
spring.jwt.issuer=licruit
TokenProvider
access 토큰이랑 refresh 토큰 만드는 코드
@Component
public class TokenProvider {
private final String issuer;
private final SecretKey secretKey;
private final long accessTokenExpireTime;
private final long refreshTokenExpireTime;
private final JwtParser jwtParser;
public TokenProvider(
@Value("${spring.jwt.issuer}") String issuer,
@Value("${spring.jwt.secret}") String secretKey,
@Value("${spring.jwt.accessTokenExpireTime}") long accessTokenExpireTime,
@Value("${spring.jwt.refreshTokenExpireTime}") long refreshTokenExpireTime
) {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
this.issuer = issuer;
this.secretKey = Keys.hmacShaKeyFor(keyBytes);
this.accessTokenExpireTime = accessTokenExpireTime;
this.refreshTokenExpireTime = refreshTokenExpireTime;
this.jwtParser = Jwts.parserBuilder().setSigningKey(keyBytes).requireIssuer(issuer).build();
}
public String createAccessToken(UserEntity userEntity) {
Date now = new Date();
Date expireDate = new Date(now.getTime() + accessTokenExpireTime);
return Jwts.builder()
.setIssuer(issuer)
.setIssuedAt(now)
.setExpiration(expireDate)
.claim("companyNumber", userEntity.getCompanyNumber())
.claim("tokenType", "accessToken")
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}
public String createRefreshToken(UserEntity userEntity) {
Date now = new Date();
Date expireDate = new Date(now.getTime() + refreshTokenExpireTime);
return Jwts.builder()
.setIssuer(issuer)
.setIssuedAt(now)
.setExpiration(expireDate)
.claim("companyNumber", userEntity.getCompanyNumber())
.claim("tokenType", "refreshToken")
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}
public TokenDTO createTokens(UserEntity userEntity) {
return TokenDTO.builder()
.accessToken(createAccessToken(userEntity))
.refreshToken(createRefreshToken(userEntity))
.build();
}
}
로그인
UserRepository
해당 사업자번호를 가진 유저가 없을 수도 있기 때문에 Optional로 한다.
@Repository
public interface UserRepository extends JpaRepository<UserEntity, String> {
// ...
Optional<UserEntity> findByCompanyNumber(String companyNumber);
}
UserService
일단 해당 사업자번호를 가진 유저를 가져온다. 만약 유저가 없으면 에러를 던진다.
원래 AuthenticationException을 바로 던지려고 했는데 빨간 줄이 그였다. 구글링 하니까 상속받아서 하라길래 그렇게 하니까 되기는 하는데 왜 그런지는 잘.... 모르겠다....
토큰 만들고 DB에 refresh 토큰 저장하고, 컨트롤러에서 토큰을 응답으로 보내줄 수 있도록 로그인 응답 객체를 리턴한다.
@Service
@RequiredArgsConstructor
public class UserService {
// ...
public LoginResponse login(LoginRequest loginRequest) {
UserEntity user = userRepository.findByCompanyNumber(loginRequest.getCompanyNumber()).orElseThrow(IncorrectPasswordException::new);
if(!passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
throw new IncorrectPasswordException();
}
TokenDTO tokens = tokenProvider.createTokens(user);
TokenEntity tokenEntity = TokenEntity.toTokenEntity(loginRequest.getCompanyNumber(), tokens.getRefreshToken());
tokenRepository.save(tokenEntity);
return LoginResponse.builder()
.accessToken(tokens.getAccessToken())
.refreshToken(tokens.getRefreshToken())
.build();
}
}
UserController
참고로 LoginResponse는 아래와 같이 생겼다. 블랙리스트 여부랑 도매업체 여부도 보내줘야 하는데 그건 차차 구현할 예정이다.
여기 @Getter 안적으면 응답이 제대로 안 보내진다.
@Getter
@Builder
public class LoginResponse {
String accessToken;
String refreshToken;
}
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
// ...
@PostMapping("/login")
public ResponseEntity<LoginResponse> login(@RequestBody @Valid LoginRequest loginRequest) {
LoginResponse loginResponse = userService.login(loginRequest);
return ResponseEntity.status(HttpStatus.OK).body(loginResponse);
}
}
에러 일지
Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
이건 ResponseEntity의 자료형을 LoginResponse으로 바꾸니까 403과 함께 나온 에러다.
@Getter를 안 달아줘서 그렇다고 한다~
주저리
이것만 장장 4시간 동안 했다. 너무 느린 거 아니니...? 최종 팀플 스프링으로 했으면 맨날 밤샜을 것 같다.
연관관계 설정도 오늘 하려고 했는데 무수한 에러와 이해 못 함 이슈로 인해 내일 하기로 했다. 내일은 JPA 알아보기
'웹 프로그래밍' 카테고리의 다른 글
[Spring Boot] Licruit - 단방향 관계 매핑, 토큰 재발급 (0) | 2024.09.20 |
---|---|
[Spring Boot] Licruit - 에러 처리 (1) | 2024.09.11 |
[Spring Boot] Licruit - 회원가입(2) (0) | 2024.09.11 |
[Spring Boot] Licruit - 회원가입 (0) | 2024.09.10 |
[Spring Boot] API 생성 (0) | 2024.07.08 |