Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/origin/feature/#18-1' intoโ€ฆ
Browse files Browse the repository at this point in the history
โ€ฆ feature/#29

# Conflicts:
#	src/main/java/meltingpot/server/domain/entity/Account.java
#	src/main/java/meltingpot/server/domain/entity/Comment.java
#	src/main/java/meltingpot/server/domain/entity/Image.java
#	src/main/java/meltingpot/server/domain/entity/Post.java
#	src/main/java/meltingpot/server/domain/entity/Report.java
#	src/main/java/meltingpot/server/domain/entity/chat/Chat.java
#	src/main/java/meltingpot/server/domain/entity/chat/ChatUser.java
#	src/main/java/meltingpot/server/domain/entity/party/PartyParticipant.java
#	src/main/java/meltingpot/server/domain/entity/party/PartyWishlist.java
  • Loading branch information
runasy-koonta committed May 26, 2024
2 parents da9f3cb + e5bc462 commit da47fde
Show file tree
Hide file tree
Showing 37 changed files with 920 additions and 17 deletions.
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
implementation 'jakarta.validation:jakarta.validation-api:3.0.2'

// JWT
//implementation 'io.jsonwebtoken:jjwt:0.9.1'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'

developmentOnly 'org.springframework.boot:spring-boot-devtools'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package meltingpot.server.auth.controller;

import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import meltingpot.server.auth.controller.dto.SigninRequestDto;
import meltingpot.server.auth.controller.dto.AccountResponseDto;
import meltingpot.server.util.ResponseCode;
import meltingpot.server.util.ResponseData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.RequiredArgsConstructor;
import meltingpot.server.auth.service.AuthService;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("auth")
public class AuthController {

private final AuthService authService;
private final Logger logger = LoggerFactory.getLogger(this.getClass());

// ํšŒ์› ๊ฐ€์ž…


// ๋กœ๊ทธ์ธ
@PostMapping("signin")
@Operation(summary="๋กœ๊ทธ์ธ", description="๋กœ๊ทธ์ธ API ์ž…๋‹ˆ๋‹ค.")
public ResponseEntity<ResponseData<AccountResponseDto>> signin(
@RequestBody @Valid SigninRequestDto request
){
AccountResponseDto data = authService.signin(request.toServiceDto());
logger.info("SIGNIN_SUCCESS (200 OK) :: userId = {}, userEmail = {}",
data.getId(), data.getEmail());
return ResponseData.toResponseEntity(ResponseCode.SIGNIN_SUCCESS, data);
}


// ๋กœ๊ทธ์•„์›ƒ

// ์ด๋ฉ”์ผ ์ธ์ฆ

// ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •

// ํ† ํฐ ์žฌ๋ฐœ๊ธ‰


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package meltingpot.server.auth.controller.dto;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import meltingpot.server.domain.entity.Account;
import meltingpot.server.util.TokenDto;

@Getter
@Setter
@Builder
public class AccountResponseDto {
private final Long id;
private final String email;
private final String name;
private TokenDto tokenDto;

public static AccountResponseDto of(Account account) {
return AccountResponseDto.builder()
.id(account.getId())
.email(account.getUsername())
.name(account.getName())
.build();
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package meltingpot.server.auth.controller.dto;


import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import meltingpot.server.auth.service.dto.SigninServiceDto;

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SigninRequestDto {

@NotBlank(message = "email is required")
private String email;

@NotBlank(message = "password is required")
private String password;

public SigninServiceDto toServiceDto() {
return SigninServiceDto.builder()
.username(getEmail())
.password(getPassword())
.build();
}
}
93 changes: 93 additions & 0 deletions src/main/java/meltingpot/server/auth/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package meltingpot.server.auth.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import meltingpot.server.exception.ResourceNotFoundException;
import meltingpot.server.config.TokenProvider;
import meltingpot.server.domain.entity.RefreshToken;
import meltingpot.server.domain.entity.Account;
import meltingpot.server.domain.repository.RefreshTokenRepository;
import meltingpot.server.domain.repository.AccountRepository;
import meltingpot.server.auth.controller.dto.AccountResponseDto;
import meltingpot.server.auth.service.dto.SigninServiceDto;
import meltingpot.server.util.AccountUser;
import meltingpot.server.util.ResponseCode;
import meltingpot.server.util.SecurityUtil;
import meltingpot.server.util.TokenDto;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@RequiredArgsConstructor
@Service
@EnableWebSecurity
public class AuthService implements UserDetailsService {
private final AccountRepository accountRepository;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final TokenProvider tokenProvider;
private final RefreshTokenRepository refreshTokenRepository;
//private final PasswordEncoder passwordEncoder;
private static final String BEARER_HEADER = "Bearer ";

// ๋กœ๊ทธ์ธ ์œ ์ € ์ •๋ณด ๋ฐ˜ํ™˜ to @CurrentUser
@Transactional(readOnly = true)
public Account getUserInfo(){
return accountRepository.findByUsernameAndDeletedIsNull(SecurityUtil.getCurrentUserName())
.orElseThrow(() -> new ResourceNotFoundException(ResponseCode.ACCOUNT_NOT_FOUND));
}

// ๋กœ๊ทธ์ธ์‹œ ์œ ์ €์ •๋ณด ์กฐํšŒํ•˜๋Š” ๋ฉ”์„œ๋“œ override
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(username));
return new AccountUser(account);
}

// ๋กœ๊ทธ์ธ
@Transactional(rollbackFor = Exception.class)
public AccountResponseDto signin(SigninServiceDto serviceDto){

// 1. Login ID/PW ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ AuthenticationToken ์ƒ์„ฑ (๋ฏธ์ธ์ฆ ํ† ํฐ)
UsernamePasswordAuthenticationToken authenticationToken = serviceDto.toAuthentication();

// 2. ๊ฒ€์ฆ (์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ ์ฒดํฌ) ์ด ์ด๋ฃจ์–ด์ง€๋Š” ๋ถ€๋ถ„
// authenticate ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰์ด ๋  ๋•Œ loadUserByUsername ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋จ
Authentication authentication = authenticationManagerBuilder.getObject()
.authenticate(authenticationToken);

// 3. ์ธ์ฆ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ JWT ํ† ํฐ ์ƒ์„ฑ
TokenDto tokenDto = tokenProvider.generateTokenDto(authentication);

// 4. RefreshToken ์ €์žฅ
Account account = accountRepository.findByUsername(authentication.getName())
.orElseThrow(() -> new ResourceNotFoundException(ResponseCode.ACCOUNT_NOT_FOUND));
RefreshToken refreshToken = RefreshToken.builder()
.account(account)
.tokenValue(tokenDto.getRefreshToken())
.build();

refreshTokenRepository.save(refreshToken);

//์ธ์ฆ๋œ Authentication๋ฅผ SecurityContext์— ์ €์žฅ
SecurityContextHolder.getContext().setAuthentication(authentication);

// 5. ํ† ํฐ ํฌํ•จ ํ˜„์žฌ ์œ ์ € ์ •๋ณด ๋ฐ˜ํ™˜
AccountResponseDto accountResponseDto = AccountResponseDto.of(getUserInfo());
accountResponseDto.setTokenDto(tokenDto);

return accountResponseDto;

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package meltingpot.server.auth.service.dto;

import lombok.Builder;
import lombok.Getter;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

@Getter
@Builder
public class SigninServiceDto {

private final String username;
private final String password;

// ๋ฏธ์ธ์ฆ ํ† ํฐ ์ƒ์„ฑ
public UsernamePasswordAuthenticationToken toAuthentication() {
return new UsernamePasswordAuthenticationToken(getUsername(), getPassword());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package meltingpot.server.config;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class HttpLogoutSuccessHandler implements LogoutSuccessHandler {

@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
if (authentication == null) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
} else {
response.setStatus(HttpServletResponse.SC_OK);
}
}
}
21 changes: 21 additions & 0 deletions src/main/java/meltingpot/server/config/JwtAccessDeniedHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package meltingpot.server.config;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {

@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
// ํ•„์š”ํ•œ ๊ถŒํ•œ์ด ์—†์ด ์ ‘๊ทผํ•˜๋ ค ํ• ๋•Œ 401
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package meltingpot.server.config;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import meltingpot.server.exception.UnknownAuthenticationException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
// ์œ ํšจํ•œ ์ž๊ฒฉ์ฆ๋ช…์„ ์ œ๊ณตํ•˜์ง€ ์•Š๊ณ  ์ ‘๊ทผํ•˜๋ ค ํ• ๋•Œ 401
if (authException instanceof BadCredentialsException
|| authException instanceof InternalAuthenticationServiceException) {
throw new BadCredentialsException("์ด๋ฉ”์ผ์ด๋‚˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋งž์ง€ ์•Š์Šต๋‹ˆ๋‹ค");
} else if (authException instanceof DisabledException) {
throw new DisabledException("๊ณ„์ •์ด ๋น„ํ™œ์„ฑํ™” ๋˜์—ˆ์Šต๋‹ˆ๋‹ค");
} else if (authException instanceof CredentialsExpiredException) {
throw new CredentialsExpiredException("๋น„๋ฐ€๋ฒˆํ˜ธ ์œ ํšจ๊ธฐ๊ฐ„์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค");
} else {
throw new UnknownAuthenticationException("์•Œ ์ˆ˜ ์—†๋Š” ์ด์œ ๋กœ ๋กœ๊ทธ์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค");
}
}
}
Loading

0 comments on commit da47fde

Please sign in to comment.