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 Core] 최규원 미션 제출합니다. #74

Open
wants to merge 22 commits into
base: kyuwon-choi
Choose a base branch
from
Open
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
8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

implementation 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0'

implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.2'
implementation 'io.jsonwebtoken:jjwt-gson:0.11.2'
compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured:5.3.1'
Expand All @@ -32,3 +34,7 @@ dependencies {
test {
useJUnitPlatform()
}

tasks.withType(JavaCompile) {
options.compilerArgs << "-parameters"
}
35 changes: 35 additions & 0 deletions src/main/java/auth/AdminInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package auth;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import roomescape.member.Member;
import roomescape.member.MemberResponse;
import roomescape.member.MemberService;

@Component
public class AdminInterceptor implements HandlerInterceptor {
private final MemberService memberService;

public AdminInterceptor(MemberService memberService) {
this.memberService = memberService;
}

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

Cookie[] cookies = request.getCookies();
String token = memberService.extractTokenFromCookie(cookies);

Member member = memberService.findByToken(token);

if (member == null || !member.getRole().equals("ADMIN")) {
response.setStatus(401);
return false;
}

return true;
}
}
15 changes: 15 additions & 0 deletions src/main/java/auth/AuthConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package auth;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "auth")
public class AuthConfig {
@Bean
public JwtUtils jwtUtils(@Value("${roomescape.auth.jwt.secret}") String jwtSecret) {
return new JwtUtils(jwtSecret);
}
}
27 changes: 27 additions & 0 deletions src/main/java/auth/JwtUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package auth;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.security.auth.message.config.AuthConfigProvider;
import org.springframework.stereotype.Component;
import roomescape.member.Member;
import org.springframework.beans.factory.annotation.Value;
public class JwtUtils {
private final String secretKey;

public JwtUtils(String secretKey) {
this.secretKey = secretKey;
}
public String createToken(Member member) {

String accessToken = Jwts.builder()
.setSubject(member.getId().toString())
.claim("name", member.getName())
.claim("email", member.getEmail())
.claim("role", member.getRole())
.signWith(Keys.hmacShaKeyFor(secretKey.getBytes()))
.compact();
return accessToken;

}
}
34 changes: 34 additions & 0 deletions src/main/java/auth/LoginMemberArgumentResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package auth;

import jakarta.servlet.http.HttpServletRequest;
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 roomescape.member.LoginMember;
import roomescape.member.Member;
import roomescape.member.MemberService;

@Component
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
private MemberService memberService;

public LoginMemberArgumentResolver(MemberService memberService) {
this.memberService = memberService;
}

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

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
String token = memberService.extractTokenFromCookie(request.getCookies());
Member member = memberService.findByToken(token);
return new LoginMember(member.getId(), member.getName(), member.getEmail(), member.getRole());
}
}
11 changes: 11 additions & 0 deletions src/main/java/auth/LoginResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package auth;

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

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginResolver {
}
23 changes: 23 additions & 0 deletions src/main/java/roomescape/DataLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package roomescape;

import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import roomescape.member.Member;
import roomescape.member.MemberRepository;

@Profile("default")
@Component
public class DataLoader implements CommandLineRunner {
private final MemberRepository memberRepository;

public DataLoader(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}

@Override
public void run(final String... args) throws Exception {
final Member member1 = memberRepository.save(new Member("어드민", "[email protected]", "password", "ADMIN"));
final Member member2 = memberRepository.save(new Member("브라운", "[email protected]", "password", "USER"));
}
}
53 changes: 53 additions & 0 deletions src/main/java/roomescape/TestDataLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package roomescape;

import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import roomescape.member.Member;
import roomescape.member.MemberRepository;
import roomescape.reservation.Reservation;
import roomescape.reservation.ReservationRepository;
import roomescape.theme.Theme;
import roomescape.theme.ThemeRepository;
import roomescape.time.Time;
import roomescape.time.TimeRepository;

@Profile("test")
@Component
public class TestDataLoader implements CommandLineRunner {
private final MemberRepository memberRepository;
private final ThemeRepository themeRepository;
private final TimeRepository timeRepository;
private final ReservationRepository reservationRepository;

public TestDataLoader(MemberRepository memberRepository, ThemeRepository themeRepository, TimeRepository timeRepository, ReservationRepository reservationRepository) {
this.memberRepository = memberRepository;
this.themeRepository = themeRepository;
this.timeRepository = timeRepository;
this.reservationRepository = reservationRepository;
}

@Override
public void run(final String... args) throws Exception {
Member member1 = memberRepository.save(new Member("어드민", "[email protected]", "password", "ADMIN"));
Member member2 = memberRepository.save(new Member("브라운", "[email protected]", "password", "USER"));

Theme theme1 = themeRepository.save(new Theme("테마1", "테마1입니다."));
Theme theme2 = themeRepository.save(new Theme("테마2", "테마2입니다."));
Theme theme3 = themeRepository.save(new Theme("테마3", "테마3입니다."));

Time time1 = timeRepository.save(new Time("10:00"));
Time time2 = timeRepository.save(new Time("12:00"));
Time time3 = timeRepository.save(new Time("14:00"));
Time time4 = timeRepository.save(new Time("16:00"));
Time time5 = timeRepository.save(new Time("18:00"));
Time time6 = timeRepository.save(new Time("20:00"));

reservationRepository.save(new Reservation("어드민", "2024-03-01", time1, theme1, member1));
reservationRepository.save(new Reservation("어드민", "2024-03-01", time2, theme2, member1));
reservationRepository.save(new Reservation("어드민", "2024-03-01", time3, theme3, member1));
reservationRepository.save(new Reservation("브라운", "2024-03-01", time4, theme1, member2));

}

}
32 changes: 32 additions & 0 deletions src/main/java/roomescape/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package roomescape;

import auth.AdminInterceptor;
import auth.LoginMemberArgumentResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
@Import(auth.AuthConfig.class)
public class WebConfig implements WebMvcConfigurer {

@Autowired
private LoginMemberArgumentResolver loginMemberArgumentResolver;
@Autowired
private AdminInterceptor adminInterceptor;

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginMemberArgumentResolver);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(adminInterceptor)
.addPathPatterns("/admin/**");
}
}
20 changes: 20 additions & 0 deletions src/main/java/roomescape/member/LoginMember.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package roomescape.member;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class LoginMember {
private Long id;
private String name;
private String email;
private String role;

public LoginMember(Long id, String name, String email, String role) {
this.id = id;
this.name = name;
this.email = email;
this.role = role;
}
}
14 changes: 9 additions & 5 deletions src/main/java/roomescape/member/Member.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package roomescape.member;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String password;
private String role;

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

public Member(String name, String email, String password, String role) {
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/roomescape/member/MemberController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package roomescape.member;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
Expand All @@ -24,6 +26,32 @@ public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) {
MemberResponse member = memberService.createMember(memberRequest);
return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member);
}
@PostMapping("/login")
public ResponseEntity login(@RequestBody MemberRequest memberRequest, HttpServletResponse response) {
Member member = memberService.findMemberByEmailAndPassword(memberRequest.getEmail(), memberRequest.getPassword());
if (member == null) {
return ResponseEntity.badRequest().build();
}
String token = memberService.createToken(member);
Cookie cookie = new Cookie("token", token);
cookie.setHttpOnly(true);
cookie.setPath("/");
response.addCookie(cookie);
return ResponseEntity.ok().build();

}
@GetMapping("/login/check")
public ResponseEntity<MemberResponse> checkLogin(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies == null) {
return ResponseEntity.badRequest().build();
}
String token = memberService.extractTokenFromCookie(cookies);
Member member = memberService.findByToken(token);
MemberResponse memberResponse = new MemberResponse(member.getId(), member.getName(), member.getEmail());
return ResponseEntity.ok(memberResponse);
}


@PostMapping("/logout")
public ResponseEntity logout(HttpServletResponse response) {
Expand Down
Loading