Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

๐Ÿ”€ :: ๋กœ๊ทธ์ธ api ๊ตฌํ˜„ #34

Merged
merged 8 commits into from
Jun 18, 2024
19 changes: 19 additions & 0 deletions src/main/java/com/juicycool/backend/domain/auth/RefreshToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.juicycool.backend.domain.auth;

import com.juicycool.backend.global.security.jwt.JwtProvider;
import lombok.Builder;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.index.Indexed;

@RedisHash(value = "jusi_refreshToken", timeToLive = JwtProvider.REFRESH_TOKEN_TIME)
@Builder
@Getter
public class RefreshToken {
@Id
@Indexed
private Long userId;
@Indexed
private String token;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.juicycool.backend.domain.auth.exception;

import com.juicycool.backend.global.exception.ErrorCode;
import com.juicycool.backend.global.exception.GlobalException;

public class NotMatchPasswordException extends GlobalException {
public NotMatchPasswordException() {
super(ErrorCode.NOT_MATCH_PASSWORD);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.juicycool.backend.domain.auth.presentation;

import com.juicycool.backend.domain.auth.presentation.dto.request.SignInRequestDto;
import com.juicycool.backend.domain.auth.presentation.dto.request.SignUpRequestDto;
import com.juicycool.backend.domain.auth.presentation.dto.response.TokenResponse;
import com.juicycool.backend.domain.auth.service.SignInService;
import com.juicycool.backend.domain.auth.service.SignUpService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -16,12 +20,19 @@
public class AuthController {

private final SignUpService signUpService;
private final SignInService signInService;

@PostMapping("/signup")
public ResponseEntity<Void> signUp(@RequestBody SignUpRequestDto dto) {
public ResponseEntity<Void> signUp(@Valid @RequestBody SignUpRequestDto dto) {
signUpService.execute(dto);
return ResponseEntity.status(HttpStatus.CREATED).build();
}

@PostMapping("/signin")
public ResponseEntity<TokenResponse> signIn(@Valid @RequestBody SignInRequestDto dto) {
TokenResponse response = signInService.execute(dto);
return ResponseEntity.ok(response);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.juicycool.backend.domain.auth.presentation.dto.request;

import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class SignInRequestDto {
@NotBlank
private String email;
@NotBlank
private String password;
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package com.juicycool.backend.domain.auth.presentation.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class SignUpRequestDto {
@NotBlank
@Pattern(regexp = ".+@.+", message = "์ด๋ฉ”์ผ ํ˜•์‹์— ๋งž์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
private String email;

@NotBlank
private String name;

@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*[0-9])|(?=.*[A-Za-z])(?=.*[^A-Za-z0-9])|(?=.*[0-9])(?=.*[^A-Za-z0-9]).{8,}$", message = "๋น„๋ฐ€๋ฒˆํ˜ธ ํ˜•์‹์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
@NotBlank
private String password;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.juicycool.backend.domain.auth.presentation.dto.response;

import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
@Builder
public class TokenResponse {
private String accessToken;
private String refreshToken;
private LocalDateTime accessTokenExpiresIn;
private LocalDateTime refreshTokenExpiresIn;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.juicycool.backend.domain.auth.repository;

import com.juicycool.backend.domain.auth.RefreshToken;
import org.springframework.data.repository.CrudRepository;

public interface RefreshTokenRepository extends CrudRepository<RefreshToken, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.juicycool.backend.domain.auth.service;

import com.juicycool.backend.domain.auth.presentation.dto.request.SignInRequestDto;
import com.juicycool.backend.domain.auth.presentation.dto.response.TokenResponse;

public interface SignInService {
TokenResponse execute(SignInRequestDto dto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.juicycool.backend.domain.auth.service.impl;

import com.juicycool.backend.domain.auth.RefreshToken;
import com.juicycool.backend.domain.auth.exception.NotMatchPasswordException;
import com.juicycool.backend.domain.auth.presentation.dto.request.SignInRequestDto;
import com.juicycool.backend.domain.auth.presentation.dto.response.TokenResponse;
import com.juicycool.backend.domain.auth.repository.RefreshTokenRepository;
import com.juicycool.backend.domain.auth.service.SignInService;
import com.juicycool.backend.domain.user.User;
import com.juicycool.backend.domain.user.exception.NotFoundUserException;
import com.juicycool.backend.domain.user.repository.UserRepository;
import com.juicycool.backend.global.annotation.TransactionService;
import com.juicycool.backend.global.security.jwt.JwtProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;

@TransactionService
@RequiredArgsConstructor
public class SignInServiceImpl implements SignInService {

private final UserRepository userRepository;
private final RefreshTokenRepository refreshTokenRepository;
private final PasswordEncoder passwordEncoder;
private final JwtProvider jwtProvider;

public TokenResponse execute(SignInRequestDto dto) {
User user = userRepository.findByEmail(dto.getEmail())
.orElseThrow(NotFoundUserException::new);

if (passwordEncoder.matches(user.getPassword(), dto.getPassword()))
throw new NotMatchPasswordException();

TokenResponse tokenResponse = jwtProvider.generateTokenDto(user.getId());

saveRefreshToken(user, tokenResponse.getRefreshToken());

return tokenResponse;
}

private void saveRefreshToken(User user, String token) {
RefreshToken refreshToken = RefreshToken.builder()
.userId(user.getId())
.token(token)
.build();

refreshTokenRepository.save(refreshToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.juicycool.backend.domain.user.exception;

import com.juicycool.backend.global.exception.ErrorCode;
import com.juicycool.backend.global.exception.GlobalException;

public class NotFoundUserException extends GlobalException {
public NotFoundUserException() {
super(ErrorCode.NOT_FOUND_USER);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ public enum ErrorCode {
// mail
ALREADY_AUTHENTICATED_MAIL(400, "์ด๋ฏธ ์ธ์ฆ๋œ ๋ฉ”์ผ์ž…๋‹ˆ๋‹ค."),
EXPIRED_AUTH_CODE(401, "์ด๋ฏธ ๋งŒ๋ฃŒ๋œ ์ธ์ฆ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค."),
NOT_VERIFICATION_MAIL(401, "์ธ์ฆ๋œ ๋ฉ”์ผ์ด ์•„๋‹™๋‹ˆ๋‹ค.");
NOT_VERIFICATION_MAIL(401, "์ธ์ฆ๋œ ๋ฉ”์ผ์ด ์•„๋‹™๋‹ˆ๋‹ค."),

// user
NOT_FOUND_USER(404, "ํ•ด๋‹น ์œ ์ €๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."),
NOT_MATCH_PASSWORD(400, "๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");

private final int status;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

// auth
.requestMatchers(HttpMethod.POST, "/api/v1/auth/signup").permitAll()
.requestMatchers(HttpMethod.POST, "/api/v1/auth/signin").permitAll()

// mail
.requestMatchers(HttpMethod.POST, "/api/v1/email").permitAll()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.juicycool.backend.global.security.jwt;

import com.juicycool.backend.domain.auth.presentation.dto.response.TokenResponse;
import com.juicycool.backend.global.auth.AuthDetailsService;
import com.juicycool.backend.global.exception.ErrorCode;
import com.juicycool.backend.global.exception.GlobalException;
Expand Down Expand Up @@ -33,8 +34,8 @@
public class JwtProvider {
private static final String AUTHORITIES_KEY = "auth";
private static final String BEARER_TYPE = "Bearer ";
private static final long ACCESS_TOKEN_TIME = 1000 * 60 * 30L;
private static final long REFRESH_TOKEN_TIME = 1000 * 60 * 60 * 24 * 7L;
private static final long ACCESS_TOKEN_TIME = 60L * 15 * 4;
public static final long REFRESH_TOKEN_TIME = 60L * 60 * 24 * 7;

@Value("${jwt.secret}")
private String secretKey;
Expand All @@ -47,14 +48,14 @@ public void init(){
key = Keys.hmacShaKeyFor(keyBytes);
}

// public TokenResponse generateTokenDto(UUID id) {
// return TokenResponse.builder()
// .accessToken(generateAccessToken(id))
// .refreshToken(generateRefreshToken(id))
// .accessTokenExpiresIn(LocalDateTime.now().plusSeconds(ACCESS_TOKEN_TIME))
// .refreshTokenExpiresIn(LocalDateTime.now().plusSeconds(REFRESH_TOKEN_TIME))
// .build();
// }
public TokenResponse generateTokenDto(Long id) {
return TokenResponse.builder()
.accessToken(generateAccessToken(id))
.refreshToken(generateRefreshToken(id))
.accessTokenExpiresIn(LocalDateTime.now().plusSeconds(ACCESS_TOKEN_TIME))
.refreshTokenExpiresIn(LocalDateTime.now().plusSeconds(REFRESH_TOKEN_TIME))
.build();
}

public Long getExpiration(String accessToken) {
Claims claims = Jwts.parserBuilder()
Expand Down Expand Up @@ -113,7 +114,7 @@ public String parseRefreshToken(String refreshToken) {
}
}

public String generateAccessToken(UUID id) {
public String generateAccessToken(Long id) {
long now = (new Date()).getTime();

Date accessTokenExpiresIn = new Date(now + ACCESS_TOKEN_TIME);
Expand All @@ -127,7 +128,7 @@ public String generateAccessToken(UUID id) {
.compact();
}

public String generateRefreshToken(UUID id) {
public String generateRefreshToken(Long id) {
long now = (new Date()).getTime();

Date refreshTokenExpiresIn = new Date(now + REFRESH_TOKEN_TIME);
Expand Down
Loading