Skip to content

Commit

Permalink
Merge pull request #10 from Leets-Official/feat/#9/oauth2를-이용한-로그인-구현
Browse files Browse the repository at this point in the history
[feat] oauth2를 이용한 로그인 구현
  • Loading branch information
ehs208 authored Oct 15, 2024
2 parents 76ebe51 + af9365d commit 26b1746
Show file tree
Hide file tree
Showing 26 changed files with 544 additions and 14 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/github-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ jobs:
port: 22
key: ${{ secrets.PRIVATE_KEY }}
script: |
# 기존 컨테이너 중지 및 제거
if [ "$(sudo docker ps -q -f name=xcellent-be)" ]; then
sudo docker stop xcellent-be
sudo docker rm xcellent-be
fi
sudo docker ps
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/xcellent-be
sudo docker run -d -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/xcellent-be
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ application.yaml
application-dev.yaml
application-local.yaml
application-jwt.yaml
application-oauth.yaml
application-mail.yaml

### macOS ###
.DS_Store
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar

# 운영 및 개발에서 사용되는 환경 설정을 분리
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=dev,jwt", "/app.jar"]
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=dev,jwt,oauth,mail", "/app.jar"]
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.6'
implementation 'com.auth0:java-jwt:4.4.0'

// OAuth2
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

// SMTP, Redis
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'

Expand Down
20 changes: 18 additions & 2 deletions src/main/java/com/leets/xcellentbe/domain/user/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import com.leets.xcellentbe.domain.shared.BaseTimeEntity;
import com.leets.xcellentbe.domain.shared.UserStatus;

import com.leets.xcellentbe.domain.user.Role;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
Expand Down Expand Up @@ -75,6 +75,12 @@ public class User extends BaseTimeEntity {
@Enumerated(EnumType.STRING)
private UserStatus userStatus;

@NotNull
@Column
@Enumerated(EnumType.STRING)
private Role userRole;


@NotNull
@Column(length=4)
private int userBirthYear;
Expand All @@ -88,14 +94,15 @@ public class User extends BaseTimeEntity {
private int userBirthDay;

@Builder
private User(String customId, String email, String userName, String password, String phoneNumber, String description, int userBirthDay, int userBirthMonth, int userBirthYear) {
private User(String customId, String email, String userName, String password, String phoneNumber, String description, int userBirthDay, int userBirthMonth, int userBirthYear, String socialEmail, Role userRole) {
this.customId = customId;
this.email = email;
this.userName = userName;
this.password = password;
this.phoneNumber= phoneNumber;
this.description = description;
this.userStatus = UserStatus.ACTIVE;
this.userRole = userRole;
this.userBirthMonth = userBirthMonth;
this.userBirthYear = userBirthYear;
this.userBirthDay = userBirthDay;
Expand All @@ -115,6 +122,15 @@ public static User create(String customId, String email, String userName, String
.userBirthDay(userBirthDay)
.userBirthYear(userBirthYear)
.userBirthMonth(userBirthMonth)
.userRole(Role.USER)
.build();
}

public static User socialCreate(String email, String socialEmail) {
return User.builder()
.email(email)
.userRole(Role.GUEST)
.socialEmail(socialEmail)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
Optional<User> findByCustomId(String customId);;
Optional<User> findByRefreshToken(String refreshToken);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.leets.xcellentbe.global.auth.email;

import com.leets.xcellentbe.global.error.ErrorCode;
import com.leets.xcellentbe.global.error.exception.CommonException;

public class AuthCodeAlreadySentException extends CommonException {
public AuthCodeAlreadySentException() {
super(ErrorCode.AUTH_CODE_ALREADY_SENT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.leets.xcellentbe.global.auth.email;

import com.leets.xcellentbe.global.error.ErrorCode;
import com.leets.xcellentbe.global.error.exception.CommonException;

public class EmailCannotBeSent extends CommonException
{
public EmailCannotBeSent()
{
super(ErrorCode.EMAIL_CANNOT_BE_SENT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.leets.xcellentbe.global.auth.email;

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

@Getter
@NoArgsConstructor
public class EmailCheckDto {
@Email
private String email;
private String authNum;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.leets.xcellentbe.global.auth.email;

import lombok.Getter;

@Getter
public class EmailRequestDto {
private String email;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.leets.xcellentbe.global.auth.email;

import org.springframework.stereotype.Service;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import java.util.Random;

import com.leets.xcellentbe.global.error.exception.custom.InvalidInputValueException;

@Transactional
@RequiredArgsConstructor
@Service
@Slf4j
public class EmailService {
private final RedisService redisService;
private final JavaMailSender mailSender;
private int authNumber;

//임의의 6자리 양수를 반환합니다.
public void makeRandomNumber() {
Random r = new Random();
this.authNumber = 100000 + r.nextInt(900000);
}


//mail을 어디서 보내는지, 어디로 보내는지 , 인증 번호를 html 형식으로 어떻게 보내는지 작성합니다.
public String joinEmail(String email) throws MessagingException {
makeRandomNumber();
String toMail = email;
String title = "회원 가입 인증 이메일 입니다."; // 이메일 제목
String content =
"인증 번호는 " + authNumber + "입니다." +
"<br>" +
"인증번호를 제대로 입력해주세요"; //이메일 내용 삽입
mailSend(toMail, title, content);
return Integer.toString(authNumber);
}

//이메일을 전송합니다.
public void mailSend(String toMail, String title, String content) throws MessagingException {

if(redisService.getData(toMail)!=null){
throw new AuthCodeAlreadySentException();
}

try {
MimeMessage message = mailSender.createMimeMessage();//JavaMailSender 객체를 사용하여 MimeMessage 객체를 생성
MimeMessageHelper helper = new MimeMessageHelper(message, true,
"utf-8");// true를 전달하여 multipart 형식의 메시지를 지원하고, "utf-8"을 전달하여 문자 인코딩을 설정
helper.setTo("h");//이메일의 수신자 주소 설정
helper.setSubject(title);//이메일의 제목을 설정
helper.setText(content, true);//이메일의 내용 설정 두 번째 매개 변수에 true를 설정하여 html 설정으로한다.
mailSender.send(message);
}
catch(Exception e) {
throw new EmailCannotBeSent();
}
redisService.setDataExpire(toMail,Integer.toString(authNumber));
}

public String checkAuthNum(String email, String authNum) {
String storedAuthNum = redisService.getData(email);

if (storedAuthNum == null) {
return "인증번호가 만료되었습니다.";
}
else if(!storedAuthNum.equals(authNum)){
throw new InvalidInputValueException();
}
else {
return "인증에 성공하였습니다.";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.leets.xcellentbe.global.auth.email;

import java.time.Duration;

import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
@Service
@RequiredArgsConstructor
public class RedisService {
private static final long AUTH_CODE_EXPIRE_SECONDS = 300;

private final StringRedisTemplate redisTemplate;//Redis에 접근하기 위한 Spring의 Redis 템플릿 클래스

public String getData(String key){//지정된 키(key)에 해당하는 데이터를 Redis에서 가져오는 메서드
ValueOperations<String,String> valueOperations=redisTemplate.opsForValue();
return valueOperations.get(key);
}
public void setData(String key,String value){//지정된 키(key)에 값을 저장하는 메서드
ValueOperations<String,String> valueOperations=redisTemplate.opsForValue();
valueOperations.set(key,value);
}
public void setDataExpire(String key,String value){//지정된 키(key)에 값을 저장하고, 지정된 시간(duration) 후에 데이터가 만료되도록 설정하는 메서드
ValueOperations<String,String> valueOperations=redisTemplate.opsForValue();
Duration expireDuration=Duration.ofSeconds(AUTH_CODE_EXPIRE_SECONDS);
valueOperations.set(key,value,expireDuration);
}
public void deleteData(String key){//지정된 키(key)에 해당하는 데이터를 Redis에서 삭제하는 메서드
redisTemplate.delete(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpSe
public void saveAuthentication(User myUser) {
String password = myUser.getPassword();
// if (password == null) { // OAuth2 사용시 소셜로그인 비밀번호 생성 코드
// password = PasswordUtil.generateRandomPassword();
// password = PasswordUtils.generateRandomPassword();
// }
UserDetails userDetailsUser = org.springframework.security.core.userdetails.User.builder()
.username(myUser.getEmail())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@
import com.leets.xcellentbe.domain.user.dto.UserLoginRequestDto;
import com.leets.xcellentbe.domain.user.dto.UserSignUpRequestDto;
import com.leets.xcellentbe.domain.user.service.UserService;
import com.leets.xcellentbe.global.auth.email.EmailCheckDto;
import com.leets.xcellentbe.global.auth.email.EmailRequestDto;
import com.leets.xcellentbe.global.auth.email.EmailService;
import com.leets.xcellentbe.global.error.exception.custom.InvalidInputValueException;
import com.leets.xcellentbe.global.response.GlobalResponseDto;

import io.swagger.v3.oas.annotations.Operation;
import jakarta.mail.MessagingException;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {
private final UserService userService;
private final EmailService emailService;

@PostMapping("/register")
@Operation(summary = "회원가입", description = "회원가입을 합니다.")
Expand All @@ -34,4 +40,17 @@ public String login(@RequestBody UserLoginRequestDto userLoginRequestDto) {
// 로그인 로직 처리
return "로그인 성공";
}

@PostMapping("/email/send")
public ResponseEntity<GlobalResponseDto<String>> mailSend(@RequestBody EmailRequestDto emailRequestDto) throws
MessagingException {
emailService.joinEmail(emailRequestDto.getEmail());
return ResponseEntity.status(HttpStatus.OK).body(GlobalResponseDto.success());
}

@PostMapping("/email/check")
public ResponseEntity<GlobalResponseDto<String>> AuthCheck(@RequestBody EmailCheckDto emailCheckDto) {
return ResponseEntity.status(HttpStatus.OK).body(GlobalResponseDto.success(emailService.checkAuthNum(emailCheckDto.getEmail(), emailCheckDto.getAuthNum())));
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.leets.xcellentbe.global.auth.login.oauth;

import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
import java.util.Map;

import com.leets.xcellentbe.domain.user.Role;

import lombok.Getter;

/**
* DefaultOAuth2User를 상속하고, email과 role 필드를 추가로 가진다.
*/
@Getter
public class CustomOAuthUser extends DefaultOAuth2User {

private String email;
private Role role;

public CustomOAuthUser(Collection<? extends GrantedAuthority> authorities,
Map<String, Object> attributes, String nameAttributeKey,
String email, Role role) {
super(authorities, attributes, nameAttributeKey);
this.email = email;
this.role = role;
}
}
Loading

0 comments on commit 26b1746

Please sign in to comment.