Skip to content

Commit

Permalink
Token 처리하는 필터 구현 (#41)
Browse files Browse the repository at this point in the history
* chore(config): enable OpenFeign

* chore(config): OpenFeign 설정 변경

* chore(build): jwt 의존성 추가

* chore(yml): jwt 설정 추가

* feat(auth): 구글 auth feign 추가

* feat(auth): 구글 auth로 로그인 구현

* feat(auth): 토큰 발급

* feat(user): 유저 추가 혹은 업데이트 구현

* feat(auth): jwt credential 추가

* feat(user): email 추가

* feat(auth): exception들 추가

* feat(user): Role 제거

* feat(auth): dto 추가

* feat(user): Role 추가

* feat(profile): getCurrentUser 추가

* feat(comment): getCurrentUser 추가

* feat(like): getCurrentUser 추가

* feat(auth): 현재 사용자를 저장하는 authRepository 구현

* feat(auth): 인터셉터 구현
  • Loading branch information
jacobhboy authored Jan 14, 2024
1 parent 55f263d commit 67ef81b
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 47 deletions.
12 changes: 12 additions & 0 deletions src/main/java/com/sickgyun/server/auth/annotation/AdminOnly.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.sickgyun.server.auth.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@LoginRequired
public @interface AdminOnly {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.sickgyun.server.auth.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.sickgyun.server.auth.interceptor;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import com.sickgyun.server.auth.annotation.AdminOnly;
import com.sickgyun.server.auth.annotation.LoginRequired;
import com.sickgyun.server.auth.exception.UserIsNotAdminException;
import com.sickgyun.server.auth.repository.AuthRepository;
import com.sickgyun.server.auth.util.BearerTokenExtractor;
import com.sickgyun.server.auth.util.JwtParser;
import com.sickgyun.server.user.domain.User;
import com.sickgyun.server.user.domain.repository.UserRepository;
import com.sickgyun.server.user.domain.role.Role;
import com.sickgyun.server.user.exception.UserNotFoundException;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

@Configuration
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {
private final JwtParser jwtParser;
private final AuthRepository authRepository;
private final UserRepository userRepository;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (handler instanceof HandlerMethod hm) {
if (hm.hasMethodAnnotation(LoginRequired.class)) {
String jwt = BearerTokenExtractor.extract(request);
Long userId = jwtParser.getIdFromJwt(jwt);

User user = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(userId));

authRepository.updateCurrentUser(user);
}
if (hm.hasMethodAnnotation(AdminOnly.class)) {
User currentUser = authRepository.getCurrentUser();
shouldUserAdmin(currentUser);
}
}
return true;
}

private static void shouldUserAdmin(User currentUser) {
if (currentUser.getRole() != Role.ADMIN) {
throw new UserIsNotAdminException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.sickgyun.server.auth.repository;

import org.springframework.stereotype.Repository;

import com.sickgyun.server.auth.exception.UserNotLoginException;
import com.sickgyun.server.user.domain.User;

@Repository
public class AuthRepository {
private User currentUser;

public User getCurrentUser() {
if (currentUser == null) {
throw new UserNotLoginException();
}
return currentUser;
}

public void updateCurrentUser(User currentUser) {
this.currentUser = currentUser;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.sickgyun.server.auth.util;

import static org.springframework.http.HttpHeaders.*;

import com.sickgyun.server.auth.exception.TokenInvalidException;
import com.sickgyun.server.auth.exception.TokenMissingException;

import jakarta.servlet.http.HttpServletRequest;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BearerTokenExtractor {

private static final String BEARER_TYPE = "Bearer ";
private static final String BEARER_JWT_REGEX = "^Bearer [A-Za-z0-9-_=]+\\.[A-Za-z0-9-_=]+\\.?[A-Za-z0-9-_.+/=]*$";

public static String extract(HttpServletRequest request) {
String authorization = request.getHeader(AUTHORIZATION);
validate(authorization);
return authorization.replace(BEARER_TYPE, "").trim();
}

private static void validate(String authorization) {
if (authorization == null) {
throw new TokenMissingException();
}
if (!authorization.matches(BEARER_JWT_REGEX)) {
throw new TokenInvalidException();
}
}

}
35 changes: 35 additions & 0 deletions src/main/java/com/sickgyun/server/auth/util/JwtParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.sickgyun.server.auth.util;

import org.springframework.stereotype.Component;

import com.sickgyun.server.auth.exception.TokenExpiredException;
import com.sickgyun.server.auth.exception.TokenInvalidException;
import com.sickgyun.server.common.config.JwtCredentials;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class JwtParser {
public static final String ID = "id";
private final JwtCredentials jwtCredentials;

public Long getIdFromJwt(String jwt) {
try {
return Long.parseLong(Jwts.parserBuilder()
.setSigningKey(jwtCredentials.secretKey())
.build()
.parseClaimsJws(jwt)
.getBody()
.get(ID)
.toString());
} catch (ExpiredJwtException e) {
throw new TokenExpiredException();
} catch (JwtException e) {
throw new TokenInvalidException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.sickgyun.server.auth.repository.AuthRepository;
import com.sickgyun.server.comment.presentation.dto.CommentResponse;
import com.sickgyun.server.comment.presentation.dto.CreateCommentRequest;
import com.sickgyun.server.comment.service.CommandCommentService;
import com.sickgyun.server.comment.service.QueryCommentService;
import com.sickgyun.server.user.domain.User;
import com.sickgyun.server.user.service.UserTempService;

import lombok.RequiredArgsConstructor;

Expand All @@ -27,7 +26,7 @@
@RequestMapping("/comments")
public class CommentController {

private final UserTempService userTempService;
private final AuthRepository authRepository;
private final CommandCommentService commandCommentService;
private final QueryCommentService queryCommentService;

Expand All @@ -37,8 +36,7 @@ public void createComment(
@PathVariable(name = "qna-id") Long qnAId,
@RequestBody CreateCommentRequest request
) {
User writer = userTempService.getUserId1();
commandCommentService.createComment(qnAId, writer, request.toEntity());
commandCommentService.createComment(qnAId, authRepository.getCurrentUser(), request.toEntity());
}

@ResponseStatus(HttpStatus.NO_CONTENT)
Expand All @@ -47,8 +45,7 @@ public void updateComment(
@PathVariable(name = "comment-id") Long commentId,
@RequestBody CreateCommentRequest request
) {
User writer = userTempService.getUserId1();
commandCommentService.updateComment(commentId, request.toEntity(), writer);
commandCommentService.updateComment(commentId, request.toEntity(), authRepository.getCurrentUser());
}

@GetMapping("/{qna-id}")
Expand All @@ -61,7 +58,6 @@ public List<CommentResponse> findByQnA(@PathVariable(name = "qna-id") Long qnAId
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/{comment-id}")
public void deleteComment(@PathVariable(name = "comment-id") Long commentId) {
User writer = userTempService.getUserId1();
commandCommentService.deleteComment(commentId, writer);
commandCommentService.deleteComment(commentId, authRepository.getCurrentUser());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.sickgyun.server.common.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.sickgyun.server.auth.interceptor.AuthInterceptor;
import com.sickgyun.server.auth.repository.AuthRepository;
import com.sickgyun.server.auth.util.JwtParser;
import com.sickgyun.server.user.domain.repository.UserRepository;

import lombok.RequiredArgsConstructor;

@Configuration
@RequiredArgsConstructor
public class InterceptorConfig implements WebMvcConfigurer {
private final JwtParser jwtParser;
private final AuthRepository authRepository;
private final UserRepository userRepository;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor(jwtParser, authRepository, userRepository));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.sickgyun.server.auth.repository.AuthRepository;
import com.sickgyun.server.like.service.CommandLikeService;
import com.sickgyun.server.like.service.QueryLikeService;
import com.sickgyun.server.user.domain.User;
import com.sickgyun.server.user.service.UserTempService;

import lombok.RequiredArgsConstructor;

Expand All @@ -23,25 +22,22 @@ public class LikeController {

private final CommandLikeService commandLikeService;
private final QueryLikeService queryLikeService;
private final UserTempService userTempService;
private final AuthRepository authRepository;

@ResponseStatus(HttpStatus.CREATED)
@PostMapping("/{qna-id}")
public void createLike(@PathVariable(name = "qna-id") Long qnAId) {
User user = userTempService.getUserId1();
commandLikeService.create(qnAId, user);
commandLikeService.create(qnAId, authRepository.getCurrentUser());
}

@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/{qna-id}")
public void deleteLike(@PathVariable(name = "qna-id") Long qnAId) {
User user = userTempService.getUserId1();
commandLikeService.delete(qnAId, user);
commandLikeService.delete(qnAId, authRepository.getCurrentUser());
}

@GetMapping("/{qna-id}")
public boolean checkLiked(@PathVariable(name = "qna-id") Long qnAId) {
User user = userTempService.getUserId1();
return queryLikeService.checkLike(qnAId, user);
return queryLikeService.checkLike(qnAId, authRepository.getCurrentUser());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.sickgyun.server.auth.annotation.LoginRequired;
import com.sickgyun.server.auth.repository.AuthRepository;
import com.sickgyun.server.profile.presentation.dto.FilterRequest;
import com.sickgyun.server.profile.presentation.dto.ProfileCreateRequest;
import com.sickgyun.server.profile.presentation.dto.ProfileResponse;
import com.sickgyun.server.profile.presentation.dto.ProfileUpdateRequest;
import com.sickgyun.server.profile.presentation.dto.SimpleProfileResponse;
import com.sickgyun.server.profile.service.CommandProfileService;
import com.sickgyun.server.profile.service.QueryProfileService;
import com.sickgyun.server.user.domain.User;
import com.sickgyun.server.user.service.UserTempService;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
Expand All @@ -31,24 +31,20 @@
public class ProfileController {
private final CommandProfileService commandService;
private final QueryProfileService queryService;
private final UserTempService userTempService;
private final AuthRepository authRepository;

@PostMapping
@LoginRequired
@ResponseStatus(HttpStatus.CREATED)
public void create(@Valid @RequestBody ProfileCreateRequest requestDto) {
//TODO getCurrent User
User writer = userTempService.getUserId1();

commandService.create(requestDto.toEntity(), writer);
commandService.create(requestDto.toEntity(), authRepository.getCurrentUser());
}

@PutMapping
@ResponseStatus(HttpStatus.NO_CONTENT)
public void update(@Valid @RequestBody ProfileUpdateRequest requestDto) {
//TODO getCurrent User
User writer = userTempService.getUserId1();

commandService.update(requestDto.toEntity(), writer);
commandService.update(requestDto.toEntity(), authRepository.getCurrentUser());
}

@GetMapping
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/sickgyun/server/user/domain/User.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.sickgyun.server.user.domain;

import com.sickgyun.server.profile.domain.Profile;
import com.sickgyun.server.user.domain.role.Role;

import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
Expand All @@ -21,6 +24,9 @@ public class User {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Enumerated(EnumType.STRING)
private Role role;

private String name;

private String email;
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/sickgyun/server/user/domain/role/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sickgyun.server.user.domain.role;

public enum Role {
ADMIN,
USER
}

This file was deleted.

0 comments on commit 67ef81b

Please sign in to comment.