Skip to content

Commit

Permalink
Merge pull request #12 from depromeet/feature/6-jwt-filter
Browse files Browse the repository at this point in the history
fix: jwt 오류 해결 및 컨플릭트 해결
  • Loading branch information
its-sky authored Jul 6, 2024
2 parents 7e68eb4 + 8dbeb0a commit a01492b
Show file tree
Hide file tree
Showing 33 changed files with 881 additions and 242 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ out/

### VS Code ###
.vscode/

### .RUN ###
Application.run.xml
7 changes: 6 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ dependencies {
// implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

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

// jwt
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'

// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.depromeet.domain.auth.controller;

import static com.depromeet.global.security.constant.SecurityConstant.*;

import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
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;

import com.depromeet.domain.auth.dto.request.LoginDto;
import com.depromeet.domain.auth.dto.response.JwtTokenResponseDto;
import com.depromeet.domain.auth.service.AuthService;
import com.depromeet.domain.member.dto.request.MemberCreateDto;

import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

@Tag(name = "로그인/회원가입")
@RequestMapping("/api/v1/auth")
@RestController
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;

// 임시
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginDto loginDto) {
JwtTokenResponseDto jwtTokenResponseDto = authService.login(loginDto);

HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(REFRESH_HEADER.getValue(), jwtTokenResponseDto.refreshToken());
httpHeaders.add(ACCESS_HEADER.getValue(), jwtTokenResponseDto.accessToken());

return ResponseEntity.ok(jwtTokenResponseDto);
}

@PostMapping
public ResponseEntity<?> signUp(@RequestBody MemberCreateDto memberCreate) {
authService.signUp(memberCreate);

return ResponseEntity.ok().build();
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/depromeet/domain/auth/dto/request/LoginDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.depromeet.domain.auth.dto.request;

import java.util.Objects;

import io.swagger.v3.oas.annotations.media.Schema;

// 임시
@Schema(name = "login")
public record LoginDto(@Schema(defaultValue = "user@gmail.com") String email,
@Schema(defaultValue = "password") String password) {
public LoginDto {
Objects.requireNonNull(email);
Objects.requireNonNull(password);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.depromeet.domain.auth.dto.response;

import java.util.Objects;

public record JwtTokenResponseDto(String accessToken, String refreshToken) {
public JwtTokenResponseDto {
Objects.requireNonNull(accessToken);
Objects.requireNonNull(refreshToken);
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/depromeet/domain/auth/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.depromeet.domain.auth.service;

import com.depromeet.domain.auth.dto.request.LoginDto;
import com.depromeet.domain.auth.dto.response.JwtTokenResponseDto;
import com.depromeet.domain.member.dto.request.MemberCreateDto;

public interface AuthService {
JwtTokenResponseDto login(LoginDto loginDto);

void signUp(MemberCreateDto memberCreateDto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.depromeet.domain.auth.service;

import static com.depromeet.global.dto.type.auth.AuthErrorType.*;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.depromeet.domain.auth.dto.request.LoginDto;
import com.depromeet.domain.auth.dto.response.JwtTokenResponseDto;
import com.depromeet.domain.member.controller.port.MemberService;
import com.depromeet.domain.member.domain.Member;
import com.depromeet.domain.member.dto.request.MemberCreateDto;
import com.depromeet.global.exception.ForbiddenException;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Transactional
@RequiredArgsConstructor
@Service
public class AuthServiceImpl implements AuthService {
private final MemberService memberService;
private final JwtTokenService jwtTokenService;

@Override
public JwtTokenResponseDto login(LoginDto loginDto) {
Member member = memberService.findByEmail(loginDto.email());
boolean pwMatch = memberService.matchPassword(loginDto.password(), member.getPassword());

if (!pwMatch) {
throw new ForbiddenException(LOGIN_FAILED);
}

return jwtTokenService.generateToken(member.getId(), member.getRole());
}

@Override
public void signUp(MemberCreateDto memberCreateDto) {
Member member = memberService.save(memberCreateDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.depromeet.domain.auth.service;

import static com.depromeet.global.dto.type.auth.AuthErrorType.*;
import static com.depromeet.global.dto.type.member.MemberErrorType.*;
import static com.depromeet.global.security.constant.SecurityConstant.*;

import java.util.Optional;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.depromeet.domain.auth.dto.response.JwtTokenResponseDto;
import com.depromeet.domain.member.domain.Member;
import com.depromeet.domain.member.domain.MemberRole;
import com.depromeet.domain.member.service.port.MemberRepository;
import com.depromeet.global.exception.ForbiddenException;
import com.depromeet.global.exception.NotFoundException;
import com.depromeet.global.security.jwt.util.AccessTokenDto;
import com.depromeet.global.security.jwt.util.JwtUtils;
import com.depromeet.global.security.jwt.util.RefreshTokenDto;

import io.jsonwebtoken.ExpiredJwtException;
import jakarta.security.auth.message.AuthException;
import lombok.RequiredArgsConstructor;

@Service
@Transactional
@RequiredArgsConstructor
public class JwtTokenService {
private final JwtUtils jwtUtils;
private final MemberRepository memberRepository;

public JwtTokenResponseDto generateToken(Long memberId, MemberRole memberRole) {
AccessTokenDto accessToken = jwtUtils.generateAccessToken(memberId, memberRole);
RefreshTokenDto refreshToken = jwtUtils.generateRefreshToken(memberId);

return new JwtTokenResponseDto(BEARER_PREFIX.getValue() + accessToken.accessToken(),
BEARER_PREFIX.getValue() + refreshToken.refreshToken());
}

public Optional<AccessTokenDto> parseAccessToken(String token) {
return jwtUtils.parseAccessToken(token);
}

public Optional<RefreshTokenDto> parseRefreshToken(String token) {
return jwtUtils.parseRefreshToken(token);
}

public AccessTokenDto reissueAccessToken(String token) {
try {
parseAccessToken(token);
return null;
} catch (ExpiredJwtException e) {
Long memberId = Long.parseLong(e.getClaims().getSubject());
MemberRole memberRole = MemberRole.findByValue(e.getClaims().get("role", String.class));

return jwtUtils.generateAccessToken(memberId, memberRole);
}
}

public RefreshTokenDto reissueRefreshToken(String token) {
try {
parseRefreshToken(token);
return null;
} catch (ExpiredJwtException e) {
Long memberId = Long.parseLong(e.getClaims().getSubject());

RefreshTokenDto refreshTokenDto = jwtUtils.generateRefreshToken(memberId);
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new NotFoundException(NOT_FOUND));
member.updateRefreshToken(refreshTokenDto.refreshToken());
memberRepository.save(member);

return refreshTokenDto;
}
}

public RefreshTokenDto retrieveRefreshToken(RefreshTokenDto refreshTokenDto, String refreshToken) {
Member member = memberRepository.findById(refreshTokenDto.memberId())
.orElseThrow(() -> new NotFoundException(NOT_FOUND));
if (member.getRefreshToken().equals(refreshToken)) {
return refreshTokenDto;
}
throw new ForbiddenException(REFRESH_TOKEN_NOT_MATCH);
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
package com.depromeet.domain.member.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.depromeet.domain.member.controller.port.MemberService;
import com.depromeet.domain.member.dto.response.MemberFindOneResponseDto;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "사용자(members)")
@RestController
@RequestMapping("/api/v1/member")
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
private final MemberService memberService;

@Operation(summary = "id로 member 단일 검색")
@GetMapping("/{id}")
public MemberFindOneResponseDto getMember(@PathVariable Long id) {
return memberService.findOneMemberResponseById(id);
}

@Operation(summary = "id로 member 단일 검색")
@GetMapping("/{id}")
public MemberFindOneResponseDto getMember(@PathVariable Long id) {
return memberService.findOneMemberResponseById(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public interface MemberService {
Member findById(Long id);

Member findByEmail(String email);

boolean matchPassword(String rawPassword, String encodedPassword);
}
94 changes: 55 additions & 39 deletions src/main/java/com/depromeet/domain/member/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,64 @@

import com.depromeet.domain.member.dto.request.MemberCreateDto;
import com.depromeet.domain.member.dto.request.MemberUpdateDto;

import lombok.Builder;
import lombok.Getter;

@Getter
public class Member {
private Long id;
private String name;
private String email;
private String password;
private MemberRole role;

@Builder
public Member(Long id, String name, String email, String password, MemberRole role) {
this.id = id;
this.name = name;
this.email = email;
this.password = password;
this.role = role;
}

public static Member from(MemberCreateDto memberCreate) {
return Member.builder()
.name(memberCreate.name())
.email(memberCreate.email())
.password(memberCreate.password())
.role(MemberRole.USER)
.build();
}

public Member update(MemberUpdateDto memberUpdate) {
return Member.builder()
.id(id)
.name(memberUpdate.name() == null ? name : memberUpdate.name())
.email(email)
.password(memberUpdate.password() == null ? password : memberUpdate.password())
.role(role)
.build();
}

public void encodePassword(String password) {
if(password != null) {
this.password = password;
}
}
private Long id;
private String name;
private String email;
private String password;
private MemberRole role;
private String refreshToken;

@Builder
public Member(Long id, String name, String email, String password, MemberRole role) {
this.id = id;
this.name = name;
this.email = email;
this.password = password;
this.role = role;
}

public static Member from(MemberCreateDto memberCreate) {
return Member.builder()
.name(memberCreate.name())
.email(memberCreate.email())
.password(memberCreate.password())
.role(MemberRole.USER)
.build();
}

public static Member from(String nickname, String email) {
return Member.builder()
.name(nickname)
.email(email)
.role(MemberRole.USER)
.build();
}

public Member update(MemberUpdateDto memberUpdate) {
return Member.builder()
.id(id)
.name(memberUpdate.name() == null ? name : memberUpdate.name())
.email(email)
.password(memberUpdate.password() == null ? password : memberUpdate.password())
.role(role)
.build();
}

public void encodePassword(String password) {
if (password != null) {
this.password = password;
}
}

public void updateRefreshToken(String refreshToken) {
if (refreshToken != null) {
this.refreshToken = refreshToken;
}
}
}
Loading

0 comments on commit a01492b

Please sign in to comment.