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

[Spring MVC (인증)] 주민기 미션 제출합니다. #123

Open
wants to merge 21 commits into
base: mingking2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roomescape.auth;
package roomescape.auth.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -9,6 +9,9 @@
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import roomescape.auth.service.AuthService;
import roomescape.auth.dto.request.LoginRequest;
import roomescape.auth.dto.response.TokenResponse;
import roomescape.auth.util.CookieProvider;

@RestController
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roomescape.auth;
package roomescape.auth.dto.request;

public record LoginRequest (
String email,
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/roomescape/auth/dto/response/LoginMember.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package roomescape.auth.dto.response;


public record LoginMember(
Long id,
String name,
String email,
String role
) {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roomescape.auth.jwt;
package roomescape.auth.dto.response;

public record MemberTokenDto (
Long id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roomescape.auth;
package roomescape.auth.dto.response;

public record TokenResponse (
String token
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package roomescape.auth;
package roomescape.auth.service;

import org.springframework.stereotype.Service;

import roomescape.auth.jwt.MemberTokenDto;
import roomescape.auth.jwt.TokenService;
import roomescape.member.Member;
import roomescape.member.MemberService;
import roomescape.auth.dto.request.LoginRequest;
import roomescape.auth.dto.response.MemberTokenDto;
import roomescape.auth.dto.response.TokenResponse;
import roomescape.member.domain.Member;
import roomescape.member.service.MemberService;

@Service
public class AuthService {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package roomescape.auth.jwt;
package roomescape.auth.service;

import java.util.Date;

import javax.crypto.SecretKey;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

Expand All @@ -11,27 +13,31 @@
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import roomescape.auth.TokenResponse;
import roomescape.auth.dto.response.TokenResponse;
import roomescape.auth.dto.response.MemberTokenDto;
import roomescape.common.util.TimeProvider;

@Service
public class TokenService {
private final String secretKey;
private final SecretKey secretKey;
private final Long expiration;
private final TimeProvider timeProvider;

public TokenService(@Value("${roomescape.auth.jwt.secret.key}") String secretKey,
@Value("${roomescape.auth.jwt.secret.expiration}") Long expiration) {
this.secretKey = secretKey;
@Value("${roomescape.auth.jwt.secret.expiration}") Long expiration, TimeProvider timeProvider) {
this.secretKey = Keys.hmacShaKeyFor(secretKey.getBytes());
this.expiration = expiration;
this.timeProvider = timeProvider;
}

public TokenResponse createAccessToken(MemberTokenDto memberTokenDto) {
Date now = new Date();
Date now = timeProvider.now();
return new TokenResponse(Jwts.builder()
.setSubject(memberTokenDto.id().toString())
.claim("name", memberTokenDto.name())
.claim("role", memberTokenDto.role())
.setExpiration(createExpiration(now, expiration))
.signWith(Keys.hmacShaKeyFor(secretKey.getBytes()))
.signWith(secretKey)
.compact());
}

Expand Down Expand Up @@ -63,7 +69,7 @@ private Date createExpiration(Date now, Long expiration) {

private Claims getClaimsFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes()))
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/roomescape/common/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package roomescape.common.config;

import java.util.List;

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

import roomescape.common.interceptor.AdminInterceptor;
import roomescape.common.resolver.LoginMemberArgumentResolver;

@Configuration
public class WebConfig implements WebMvcConfigurer {

private final LoginMemberArgumentResolver loginMemberArgumentResolver;
private final AdminInterceptor adminInterceptor;

public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver, AdminInterceptor adminInterceptor) {
this.loginMemberArgumentResolver = loginMemberArgumentResolver;
this.adminInterceptor = adminInterceptor;
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginMemberArgumentResolver);
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(adminInterceptor)
.addPathPatterns("/admin/**")
.order(1);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roomescape;
package roomescape.common.exception;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/roomescape/common/interceptor/AdminInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package roomescape.common.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import roomescape.auth.dto.response.MemberTokenDto;
import roomescape.auth.service.TokenService;
import roomescape.auth.util.CookieProvider;
import roomescape.member.domain.Member;
import roomescape.member.service.MemberService;

@Component
public class AdminInterceptor implements HandlerInterceptor {
private CookieProvider cookieProvider;
private TokenService tokenService;
private MemberService memberService;

public AdminInterceptor(CookieProvider cookieProvider, TokenService tokenService, MemberService memberService) {
this.cookieProvider = cookieProvider;
this.tokenService = tokenService;
this.memberService = memberService;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interceptor 는 왜 boolean 을 return 하게 하는거 같나요?

Exception {

Cookie[] cookies = request.getCookies();
String token = cookieProvider.extractTokenFromCookie(cookies)
.orElse(null);

if (token == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sendError 를 사용했네요. setStatus 랑 차이점이 뭔가요?

return false;
}

MemberTokenDto memberTokenDto = tokenService.extractMemberResponseFromToken(token);
Member member = memberService.findMemberByName(memberTokenDto.name());
if (member == null || !member.getRole().equals("ADMIN")) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ADMIN 인지 Member 가 스스로 알려줘도 괜찮을거 같아요.

response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package roomescape.common.resolver;

import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import roomescape.auth.dto.response.MemberTokenDto;
import roomescape.auth.service.TokenService;
import roomescape.auth.util.CookieProvider;
import roomescape.auth.dto.response.LoginMember;
import roomescape.member.domain.Member;
import roomescape.member.service.MemberService;

@Component
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {

private MemberService memberService;
private TokenService tokenService;
private CookieProvider cookieProvider;

public LoginMemberArgumentResolver(MemberService memberService, TokenService tokenService, CookieProvider cookieProvider) {
this.memberService = memberService;
this.tokenService = tokenService;
this.cookieProvider = cookieProvider;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(LoginMember.class);
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
HttpServletRequest httpServletRequest = (HttpServletRequest) webRequest.getNativeRequest();
Cookie[] cookies = httpServletRequest.getCookies();
String token = cookieProvider.extractTokenFromCookie(cookies)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위 Interceptor 에서는 Null 을 받아 처리했고, 여기선 에외를 던졌네요.
두개의 차이점이 있나요? ( 의견 묻는겁니당 )

.orElseThrow(() -> new IllegalArgumentException("쿠키에 토큰이 존재하지 않습니다."));

MemberTokenDto memberTokenDto = tokenService.extractMemberResponseFromToken(token);
Member member = memberService.findMemberByName(memberTokenDto.name());
return new LoginMember(member.getId(), member.getName(), member.getEmail(), member.getRole());
}
}
13 changes: 13 additions & 0 deletions src/main/java/roomescape/common/util/DefaultTimeProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package roomescape.common.util;

import java.util.Date;

import org.springframework.stereotype.Component;

@Component
public class DefaultTimeProvider implements TimeProvider {
@Override
public Date now() {
return new Date();
}
}
7 changes: 7 additions & 0 deletions src/main/java/roomescape/common/util/TimeProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package roomescape.common.util;

import java.util.Date;

public interface TimeProvider {
Date now();
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package roomescape.member;
package roomescape.member.controller;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import roomescape.member.service.MemberService;
import roomescape.member.dto.request.MemberRequest;
import roomescape.member.dto.response.MemberResponse;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roomescape.member;
package roomescape.member.domain;

public class Member {
private Long id;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roomescape.member;
package roomescape.member.dto.request;

public class MemberRequest {
private String name;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roomescape.member;
package roomescape.member.dto.response;

public class MemberResponse {
private Long id;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package roomescape.member;
package roomescape.member.repository;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;

import roomescape.member.domain.Member;

@Repository
public class MemberDao {
private JdbcTemplate jdbcTemplate;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package roomescape.member;
package roomescape.member.service;

import org.springframework.stereotype.Service;

import roomescape.member.repository.MemberDao;
import roomescape.member.domain.Member;
import roomescape.member.dto.request.MemberRequest;
import roomescape.member.dto.response.MemberResponse;

@Service
public class MemberService {
private MemberDao memberDao;
Expand All @@ -18,4 +23,8 @@ public MemberResponse createMember(MemberRequest memberRequest) {
public Member findMemberByEmailAndPassword(String email, String password) {
return memberDao.findByEmailAndPassword(email, password);
}

public Member findMemberByName(String name) {
return memberDao.findByName(name);
}
}
Loading