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

MVP 2.0 #105

Merged
merged 16 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ddl.sql
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ CREATE TABLE member.info (
image_path varchar(300),
created_date timestamp,
follow_cnt int8 not null default 0,
follower_cnt int8 not null default 0
follower_cnt int8 not null default 0,
is_deleted bool not null default false
);

create table comment.info(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package Funssion.Inforum.common.exception.etc;

import Funssion.Inforum.common.exception.response.ErrorResult;
import org.springframework.http.HttpStatus;

public class ValueTooLongException extends RuntimeException{
private ErrorResult errorResult;
private String message;

public ValueTooLongException(String message) {
this.message = message;
this.errorResult = new ErrorResult(HttpStatus.INTERNAL_SERVER_ERROR, message);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import Funssion.Inforum.common.exception.etc.DuplicateException;
import Funssion.Inforum.common.exception.etc.ImageIOException;
import Funssion.Inforum.common.exception.etc.UnAuthorizedException;
import Funssion.Inforum.common.exception.etc.ValueTooLongException;
import Funssion.Inforum.common.exception.notfound.NotFoundException;
import Funssion.Inforum.common.exception.response.ErrorResult;
import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.postgresql.util.PSQLException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.validation.FieldError;
Expand Down Expand Up @@ -67,6 +69,13 @@ public Map<String, List<String>> handleValidationEx(MethodArgumentNotValidExcept
public ErrorResult handleDataIntegrityViolationOfJSONB(DataIntegrityViolationException e){
return new ErrorResult(BAD_REQUEST,e.getMessage());
}

@ResponseStatus(BAD_REQUEST)
@ExceptionHandler(ValueTooLongException.class)
public ErrorResult handleDataIntegrityViolationOfJSONB(ValueTooLongException e){
return new ErrorResult(BAD_REQUEST,e.getMessage());
}

@ResponseStatus(BAD_REQUEST)
@ExceptionHandler(TypeMismatchException.class)
public ErrorResult handleTypeMismatchEx (TypeMismatchException e) {
Expand Down Expand Up @@ -101,6 +110,11 @@ public ErrorResult handleDuplicateEx(DuplicateException e){
return e.getErrorResult();
}

@ResponseStatus(BAD_REQUEST)
@ExceptionHandler(PSQLException.class)
public ErrorResult handlePSQLException(PSQLException e){
return new ErrorResult(BAD_REQUEST, "DB exception occurred");
}
@ResponseStatus(INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class)
public ErrorResult handleGeneralEx(Throwable e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@
public abstract class SecurityContextUtils {
public static final Long ANONYMOUS_USER_ID = 0L;
public static final String ANONYMOUS_USER_ID_STRING = "0";
public static final String ANONYMOUS_USER_NAME = "anonymousUser";

public static Long getUserId() {
String userId = SecurityContextHolder.getContext().getAuthentication().getName();
if (userId.equals("anonymousUser")) return ANONYMOUS_USER_ID;
if (userId.equals(ANONYMOUS_USER_NAME)) return ANONYMOUS_USER_ID;
return Long.valueOf(userId);
}

public static Long getAuthorizedUserId() {
String userId = SecurityContextHolder.getContext().getAuthentication().getName();
if (userId.equals("anonymousUser")) throw new UnAuthorizedException("인증되지 않은 사용자입니다.");
if (userId.equals(ANONYMOUS_USER_NAME)) throw new UnAuthorizedException("인증되지 않은 사용자입니다.");
return Long.valueOf(userId);
}
}
4 changes: 2 additions & 2 deletions src/main/java/Funssion/Inforum/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti
authorizeRequests
.requestMatchers(HttpMethod.OPTIONS, "/**/*").permitAll()
//users 포함한 end point 보안 적용 X
.requestMatchers("/users/**").permitAll()
.requestMatchers(HttpMethod.GET,"/users/**").permitAll()
.requestMatchers(HttpMethod.GET, "/users/profile/**").permitAll() // 개인 정보 수정은 권한 필요
.requestMatchers(HttpMethod.POST, "/users/login").authenticated() //spring security filter에서 redirect
.requestMatchers(HttpMethod.GET,"/tags/**").permitAll()
.requestMatchers("/oauth2/authorization/**").permitAll()
.requestMatchers("/login/oauth2/code/**").permitAll()
.requestMatchers(HttpMethod.GET, "/users/profile/**").permitAll() // 개인 정보 수정은 권한 필요
.requestMatchers("/error/**").permitAll()
.requestMatchers(HttpMethod.GET, "/memos/**").permitAll()
.requestMatchers(HttpMethod.GET,"/questions/**").permitAll()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package Funssion.Inforum.domain.member.controller;


import Funssion.Inforum.common.constant.CRUDType;
import Funssion.Inforum.common.dto.IsSuccessResponseDto;
import Funssion.Inforum.common.exception.badrequest.BadRequestException;
import Funssion.Inforum.common.exception.notfound.NotFoundException;
import Funssion.Inforum.common.utils.SecurityContextUtils;
import Funssion.Inforum.domain.member.dto.request.*;
import Funssion.Inforum.domain.member.dto.response.*;
import Funssion.Inforum.domain.member.entity.MemberProfileEntity;
import Funssion.Inforum.domain.member.service.MailService;
import Funssion.Inforum.domain.member.service.MemberService;
import Funssion.Inforum.domain.member.dto.request.PasswordUpdateDto;
import Funssion.Inforum.domain.post.utils.AuthUtils;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
Expand All @@ -24,6 +25,7 @@
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
Expand All @@ -45,8 +47,8 @@ public class MemberController {

@PostMapping("")
@ResponseStatus(HttpStatus.CREATED)
public SaveMemberResponseDto create(@RequestBody @Valid MemberSaveDto memberSaveDto){ //dto로 바꿔야함
return memberService.requestMemberRegistration(memberSaveDto);
public SaveMemberResponseDto create(HttpServletRequest request, HttpServletResponse response, @RequestBody @Valid MemberSaveDto memberSaveDto) throws IOException { //dto로 바꿔야함
return memberService.requestMemberRegistration(memberSaveDto,request,response);
}

@PostMapping("/authenticate-email")
Expand Down Expand Up @@ -89,7 +91,6 @@ public IsSuccessResponseDto registerName(@PathVariable("id") String userId,@Requ
@GetMapping("/check")
public ValidMemberDto method(@CurrentSecurityContext SecurityContext context) {
String userId = context.getAuthentication().getName();
log.info("user id = {}",userId);
Long loginId = userId.equals("anonymousUser") ? -1L : Long.valueOf(userId);
boolean isLogin = !userId.equals("anonymousUser");
return new ValidMemberDto(loginId, isLogin);
Expand All @@ -98,22 +99,14 @@ public ValidMemberDto method(@CurrentSecurityContext SecurityContext context) {
@ResponseStatus(HttpStatus.NO_CONTENT)
@GetMapping("/logout")
public void logout(HttpServletRequest request, HttpServletResponse response) {
AuthUtils.logout(request, response);
}

Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("accessToken".equals(cookie.getName())) {
log.info("[Logout] User Id ={},", cookie.getValue());
}
else if ("refreshToken".equals(cookie.getName())){
log.info("[Logout] refresh token invalidated");
}
}
}
ResponseCookie invalidateAccessCookie = ResponseCookie.from("accessToken", "none").maxAge(0).path("/").domain(".inforum.me").sameSite("none").httpOnly(true).secure(true).build();
ResponseCookie invalidateRefreshCookie = ResponseCookie.from("refreshToken", "none").maxAge(0).path("/").domain(".inforum.me").sameSite("none").httpOnly(true).secure(true).build();
response.addHeader("Set-Cookie", invalidateAccessCookie.toString());
response.addHeader("Set-Cookie",invalidateRefreshCookie.toString());
@PostMapping("/withdraw")
public void withdraw(HttpServletRequest request, HttpServletResponse response) {
Long userId = SecurityContextUtils.getAuthorizedUserId();
memberService.withdrawUser(userId);
AuthUtils.logout(request, response);
}

@PostMapping("/profile/{id}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ public MemberProfileEntity(String profileImageFilePath, String nickname, String
this.userTags = userTags;
}

public MemberProfileEntity(String profileImageFilePath, String nickname, String introduce) {
this.profileImageFilePath = profileImageFilePath;
this.nickname = nickname;
this.introduce = introduce;
}

public static RowMapper<MemberProfileEntity> MemberInfoRowMapper() {
return ((rs, rowNum) ->
MemberProfileEntity.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ public interface MemberRepository {
IsSuccessResponseDto findAndChangePassword(PasswordUpdateDto passwordUpdateDto);

String findEmailByAuthCode(String code);

void deleteUser(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public SaveMemberResponseDto save(SocialMember member) {
}

public Optional<NonSocialMember> findNonSocialMemberByEmail(String email) {
String sql ="SELECT A.ID AS A_ID ,U.ID AS U_ID,A.PASSWORD,U.EMAIL,U.FOLLOW_CNT,U.FOLLOWER_CNT FROM member.info AS U JOIN MEMBER.AUTH AS A ON U.ID = A.USER_ID WHERE U.EMAIL = ?";
String sql ="SELECT A.ID AS A_ID ,U.ID AS U_ID,A.PASSWORD,U.EMAIL,U.FOLLOW_CNT,U.FOLLOWER_CNT FROM member.info AS U JOIN MEMBER.AUTH AS A ON U.ID = A.USER_ID WHERE U.IS_DELETED = false AND U.EMAIL = ?";
try{
NonSocialMember nonSocialMember = jdbcTemplate.queryForObject(sql,nonSocialmemberRowMapper(),email);
return Optional.of(nonSocialMember);
Expand All @@ -68,7 +68,7 @@ public Optional<NonSocialMember> findNonSocialMemberByEmail(String email) {
}
}
public Optional<SocialMember> findSocialMemberByEmail(String email){
String sql ="SELECT ID,NAME,EMAIL,LOGIN_TYPE,CREATED_DATE,IMAGE_PATH,INTRODUCE,TAGS,FOLLOW_CNT,FOLLOWER_CNT FROM member.info WHERE EMAIL = ?";
String sql ="SELECT ID,NAME,EMAIL,LOGIN_TYPE,CREATED_DATE,IMAGE_PATH,INTRODUCE,TAGS,FOLLOW_CNT,FOLLOWER_CNT FROM member.info WHERE is_deleted = false and EMAIL = ?";
try{
SocialMember socialMember = jdbcTemplate.queryForObject(sql,socialMemberRowMapper(),email);
return Optional.of(socialMember);
Expand All @@ -80,7 +80,7 @@ public Optional<SocialMember> findSocialMemberByEmail(String email){

@Override
public Optional<Member> findByName(String name) {
String sql ="SELECT ID,EMAIL,NAME FROM member.info WHERE NAME = ?";
String sql ="SELECT ID,EMAIL,NAME FROM member.info WHERE IS_DELETED = false AND NAME = ?";

try{
Member member = jdbcTemplate.queryForObject(sql,memberEmailAndNameRowMapper(),name);
Expand All @@ -102,7 +102,7 @@ public IsSuccessResponseDto saveSocialMemberNickname(String nickname,Long userId

@Override
public String findEmailByNickname(String nickname) {
String sql ="select email from member.info where name = ?";
String sql ="select email from member.info where is_deleted = false and name = ?";
try{
return jdbcTemplate.queryForObject(sql, String.class, nickname);
}catch(EmptyResultDataAccessException e){
Expand Down Expand Up @@ -217,6 +217,15 @@ private SaveMemberResponseDto saveSocialMemberInUserTable(SocialMember member) {
.build();
}

@Override
public void deleteUser(Long userId) {
String sql = "update member.info set is_deleted = true where id = ?";

if (jdbcTemplate.update(sql, userId) != 1) {
throw new BadRequestException("fail in deleting user : userId = " + userId);
}
}

private RowMapper<NonSocialMember> nonSocialmemberRowMapper(){
return new RowMapper<NonSocialMember>() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public class AuthService implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException {
log.info("useremail = {}",userEmail);
NonSocialMember member = nonSocialMemberRepository.findNonSocialMemberByEmail(userEmail)
.orElseThrow(() -> new UsernameNotFoundException("이 이메일과 매칭되는 유저가 존재하지 않습니다 : " + userEmail));
// non social, social 섞어있기 때문에, user_id를 CustomUserDetail 의 id로 생성합니다. -> 토큰의 getName의 user_id가 들어갑니다.
Expand Down
Loading