Skip to content

Commit

Permalink
Merge pull request #14 from TeamUStory/feat/user
Browse files Browse the repository at this point in the history
Feat: 닉네임 조건을 추가하고, 맞지 않는 경우 자체적으로 수정해서 DB에 저장함.
  • Loading branch information
GyungA authored Jul 14, 2024
2 parents 9f2fa09 + 3696bed commit b78d948
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,16 @@ public class RegexPatterns {

// 유저 비밀번호 규칙: 숫자, 영문, 특수문자 각 1개를 포함한 8~16자. 보안상 SQL 인젝션을 막기 위해, 특수문자는 `~!@#%^*`만 허용.
public static final String PASSWORD_REG = "^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[~!@#%^*]).{8,16}$";

// 유저 닉네임 규칙 시작: 소문자(a~z), 대문자(A~Z), 숫자(0~9), 한글(ㄱ~ㅎ, ㅏ~ㅣ, 가~힣)만 가능하며, 2~10자로 구성. 특수문자를 허용하지 않음.
// 단, OAuth 회원가입 시 닉네임이 중복될 경우에만 '#'과 임의의 숫자를 이용함.
public static final int NICKNAME_MIN_LENGTH = 2;
public static final int NICKNAME_MAX_LENGTH = 10;

public static final String NICKNAME_REG_LETTER = "[a-zA-Z0-9가-힣ㄱ-ㅎㅏ-ㅣ]";
public static final String NICKNAME_REG_LETTER_FOR_REPLACE = "[^" + NICKNAME_REG_LETTER.substring(1);
public static final String NICKNAME_REG_FULL = "^" + NICKNAME_REG_LETTER + "{" + NICKNAME_MIN_LENGTH + "," + NICKNAME_MAX_LENGTH + "}$";

// 유저 닉네임 규칙 끝

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public class UserMessageConstants {
public static final String NOT_FOUND_USER_ID_MESSAGE = "존재하지 않는 userId입니다: %d";
public static final String NOT_FOUND_USER_EMAIL_MESSAGE = "존재하지 않는 email입니다: %s";
public static final String NOT_VALID_NICKNAME_MESSAGE = "사용할 수 없는 nickname입니다: %s";
public static final String NOT_APPROPIRATE_NICKNAME_MSSAGE = "닉네임 형식이 올바르지 않습니다.";
public static final String DUPLICATE_EMAIL_MESSAGE = "이미 가입된 email입니다: %s";
public static final String NOT_CREATED_DIARY_MESSAGE = "다음의 email로 가입 중인 유저의, 개인 다이어리를 생성하는 과정에서 문제가 발생하였습니다. 가입 정보는 저장되지 않습니다: %s";
public static final String UNAUTHORIZED_MESSAGE = "헤더에 토큰이 입력되지 않았습니다.";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.elice.ustory.domain.user.dto;

import com.elice.ustory.domain.user.constant.RegexPatterns;
import com.elice.ustory.domain.user.constant.UserMessageConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
Expand All @@ -15,6 +16,7 @@ public class SignUpRequest {
private String email; // 인증 여부: done

@NotEmpty
@Pattern(regexp = RegexPatterns.NICKNAME_REG_FULL, message = UserMessageConstants.NOT_APPROPIRATE_NICKNAME_MSSAGE)
@Schema(requiredMode = REQUIRED, description = "빈 스트링은 허용되지 않습니다.", example = "피카츄")
private String name; // 인증 여부: to do

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.elice.ustory.domain.user.dto;

import com.elice.ustory.domain.user.constant.RegexPatterns;
import com.elice.ustory.domain.user.constant.UserMessageConstants;
import jakarta.validation.constraints.Pattern;
import lombok.Data;

@Data
public class ValidateNicknameRequest {
// TODO: 닉네임 규칙 생길 경우, 별도 메서드 없이 dto에서 확인
@Pattern(regexp = RegexPatterns.NICKNAME_REG_FULL, message = UserMessageConstants.NOT_APPROPIRATE_NICKNAME_MSSAGE)
private String nickname;
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public class Users extends BaseEntity {
private String name;

@Column(unique = true, columnDefinition = "VARCHAR(30) UNIQUE")
//TODO: 닉네임 제한 왜 30자?
private String nickname;
//TODO: 닉네임 제한 다시 걸기(sql injection 대비. 허용되지 않은 값은 제외하고 받아오도록.)

@Column(name = "password", columnDefinition = "VARCHAR(100)")
private String password;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
import com.elice.ustory.domain.user.repository.UserRepository;
import com.elice.ustory.global.exception.model.*;
import com.elice.ustory.global.jwt.JwtTokenProvider;
import com.elice.ustory.global.oauth.kakao.KakaoService;
import com.elice.ustory.global.oauth.naver.NaverService;
import com.elice.ustory.global.redis.naver.NaverTokenService;
import com.elice.ustory.global.redis.refresh.RefreshTokenService;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.transaction.Transactional;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class KakaoController {
@ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)))
})
@RequestMapping(value = "/login/oauth2/code/kakao", method = {RequestMethod.GET, RequestMethod.POST})
public ResponseEntity<LoginResponse> kakaoLogin(@RequestParam String code, HttpServletResponse response) {
public ResponseEntity<LoginResponse> kakaoLogin(@RequestParam(name = "code") String code, HttpServletResponse response) {
String kakaoAccessToken = kakaoOauth.getKakaoAccessToken(code);
Map<String, Object> userInfo = kakaoOauth.getUserInfoFromKakaoToken(kakaoAccessToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import com.elice.ustory.global.jwt.JwtUtil;
import com.elice.ustory.global.redis.kakao.KakaoTokenService;
import com.elice.ustory.global.redis.refresh.RefreshTokenService;
import com.elice.ustory.global.util.RandomGenerator;
import com.elice.ustory.global.util.NicknameGenerator;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -36,19 +36,19 @@ public class KakaoService {
private final KakaoTokenService kakaoTokenService;
private final JwtUtil jwtUtil;
private final KakaoOauth kakaoOauth;
private final RandomGenerator randomGenerator;
private final NicknameGenerator nicknameGenerator;
private final PasswordEncoder passwordEncoder;

public void kakaoSignUp(String kakaoUserId, String kakaoNickname){
String randomPassword = String.valueOf(UUID.randomUUID()).substring(0,8);
String encodedPassword = passwordEncoder.encode(randomPassword);
String generatedNickname = kakaoNickname + "#" + randomGenerator.generateRandomPostfix();
String formattedNickname = nicknameGenerator.formatNickname(kakaoNickname);

Users builtUser = Users.addUserBuilder()
.email(kakaoUserId+"@ustory.com")
.loginType(Users.LoginType.KAKAO)
.name(kakaoNickname)
.nickname(generatedNickname)
.name(formattedNickname)
.nickname(formattedNickname)
.password(encodedPassword)
.profileImgUrl("")
.profileDescription("자기소개")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@
import com.elice.ustory.domain.user.dto.LoginResponse;
import com.elice.ustory.domain.user.entity.Users;
import com.elice.ustory.domain.user.repository.UserRepository;
import com.elice.ustory.domain.user.service.UserService;
import com.elice.ustory.global.exception.model.NotFoundException;
import com.elice.ustory.global.jwt.JwtTokenProvider;
import com.elice.ustory.global.jwt.JwtUtil;
import com.elice.ustory.global.redis.naver.NaverTokenService;
import com.elice.ustory.global.redis.refresh.RefreshTokenService;
import com.elice.ustory.global.util.RandomGenerator;
import com.elice.ustory.global.util.NicknameGenerator;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -35,19 +33,19 @@ public class NaverService {
private final RefreshTokenService refreshTokenService;
private final NaverTokenService naverTokenService;
private final JwtTokenProvider jwtTokenProvider;
private final RandomGenerator randomGenerator;
private final NicknameGenerator nicknameGenerator;
private final PasswordEncoder passwordEncoder;

public void naverSignUp(String naverNickname, String naverEmail){
String randomPassword = String.valueOf(UUID.randomUUID()).substring(0,8);
String encodedPassword = passwordEncoder.encode(randomPassword);
String generatedNickname = naverNickname + "#" + randomGenerator.generateRandomPostfix();
String formattedNickname = nicknameGenerator.formatNickname(naverNickname);

Users builtUser = Users.addUserBuilder()
.email(naverEmail)
.loginType(Users.LoginType.NAVER)
.name(naverNickname)
.nickname(generatedNickname)
.name(formattedNickname)
.nickname(formattedNickname)
.password(encodedPassword)
.profileImgUrl("")
.profileDescription("자기소개")
Expand Down
86 changes: 86 additions & 0 deletions src/main/java/com/elice/ustory/global/util/NicknameGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.elice.ustory.global.util;

import com.elice.ustory.domain.user.constant.RegexPatterns;
import com.elice.ustory.domain.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.Random;

@Component
@RequiredArgsConstructor
public class NicknameGenerator {
private final UserRepository userRepository;

private static final int NICKNAME_MAX_LENGTH = RegexPatterns.NICKNAME_MAX_LENGTH;
public static final String SEPARATOR = "#"; // 닉네임과 임의값 사이의 구분자

private static final int POSTFIX_LENGTH = 2;
private static final int POSTFIX_LENGTH_WITH_SEPARATOR = POSTFIX_LENGTH + 1;

public String formatNickname(String nickname) {
// 닉네임에 포함된 특수문자를 제거한다.
String formattedNickname = normalizeNicknameForOAuth(nickname);
// 닉네임을 규정된 길이에 맞게 자른다.
formattedNickname = trimNicknameForOAuth(nickname);
// 중복 여부를 조회한다. 중복이면 닉네임을 7자 이내로 자르고, 겹치지 않을 때까지 임의의 postfix 3글자를 만들어 붙인다.
if (checkDuplicateNickname(nickname)) {
formattedNickname = trimNicknameForPostfix(formattedNickname);

do {
formattedNickname = formattedNickname + SEPARATOR + generateRandomPostfix();
} while (checkDuplicateNickname(formattedNickname));
}

return formattedNickname;
}

public String generateRandomPostfix() {
int leftLimit = 48; // 숫자 '0'의 ASCII 코드
int rightLimit = 122; // 알파벳 'z'의 ASCII 코드
int postfixLength = POSTFIX_LENGTH;
Random random = new Random();

return random.ints(leftLimit, rightLimit + 1) // leftLimit(포함) 부터 rightLimit+1(불포함) 사이의 난수 스트림 생성
.filter(i -> (i < 57 || i >= 65) && ( i <= 90 || i >= 97)) // ASCII 테이블에서 숫자, 대문자, 소문자만 사용함
.limit(postfixLength) // 생성된 난수를 지정된 길이로 잘라냄
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) // 생성된 난수를 ASCII 테이블에서 대응되는 문자로 변환
.toString(); // StringBuilder 객체를 문자열로 변환해 반환
}

public String normalizeNicknameForOAuth(String socialNickname) {
// 정규식에 맞지 않는 글자를 제외
String cleanedNickname = socialNickname.replaceAll(RegexPatterns.NICKNAME_REG_LETTER_FOR_REPLACE, "");
return cleanedNickname;
}

public String 임의의값을_하는_함수(String cleanedNickname) {
//TODO
return "String";
}

public String trimNicknameForOAuth(String cleanedNickname) {
if (cleanedNickname == null) {
return null;
}
String trimmedNickname = cleanedNickname.length() > NICKNAME_MAX_LENGTH ? cleanedNickname.substring(0, NICKNAME_MAX_LENGTH) : cleanedNickname;
return trimmedNickname;
}

public String trimNicknameForPostfix(String nickname) {
if (nickname == null) {
return null;
}
int availableLength = NICKNAME_MAX_LENGTH - POSTFIX_LENGTH_WITH_SEPARATOR;
String trimmedNickname = nickname.length() > (availableLength) ? nickname.substring(0, availableLength) : nickname;
return trimmedNickname;
}

public boolean checkDuplicateNickname(String nickname) {
if (userRepository.countByNicknameWithSoftDeleted(nickname) > 0) {
return true;
} else {
return false;
}
}
}
21 changes: 0 additions & 21 deletions src/main/java/com/elice/ustory/global/util/RandomGenerator.java

This file was deleted.

0 comments on commit b78d948

Please sign in to comment.