Skip to content

Commit

Permalink
UMC-EWHA#32 [Week9] jwt 설정
Browse files Browse the repository at this point in the history
  • Loading branch information
hyeonjeongs committed Nov 25, 2022
1 parent e89c6d3 commit cfc7449
Show file tree
Hide file tree
Showing 30 changed files with 604 additions and 24 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ dependencies {
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// jwt
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.umc.umcserver.domain.auth.controller;

import com.umc.umcserver.domain.auth.dto.LoginRequestDto;
import com.umc.umcserver.domain.auth.dto.LoginResponseDto;
import com.umc.umcserver.domain.auth.service.AuthService;
import com.umc.umcserver.global.dto.DtoMetaData;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
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;

@RequiredArgsConstructor
@RequestMapping("/auth")
@RestController
public class AuthController {
private final AuthService authService;

// 로그인
@PostMapping("/login")
public ResponseEntity<LoginResponseDto> login(@RequestBody LoginRequestDto requestDto) {
DtoMetaData dtoMetaData;

try {
String token = authService.login(requestDto);
dtoMetaData = new DtoMetaData("로그인 성공");
return ResponseEntity.ok(new LoginResponseDto(dtoMetaData, token));
} catch (Exception e) {
dtoMetaData = new DtoMetaData(e.getMessage(), e.getClass().getName());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new LoginResponseDto(dtoMetaData));
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.umc.umcserver.domain.auth.dto;

import lombok.Data;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

@Data
public class LoginRequestDto {

private String email;
private String password;

public UsernamePasswordAuthenticationToken toAuthentication() {
return new UsernamePasswordAuthenticationToken(email, password);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.umc.umcserver.domain.auth.dto;

import com.umc.umcserver.global.dto.DtoMetaData;
import lombok.Data;

@Data
public class LoginResponseDto {

private DtoMetaData dtoMetaData;
private String token;

public LoginResponseDto(DtoMetaData dtoMetaData, String token) {
this.dtoMetaData = dtoMetaData;
this.token = token;
}

public LoginResponseDto(DtoMetaData dtoMetaData) {
this.dtoMetaData = dtoMetaData;
this.token = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.umc.umcserver.domain.auth.repository;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Getter
@NoArgsConstructor
@Entity
public class Account {
@Id
@GeneratedValue
private Long id;

@Column
private String email;

@Column
private String password;

@Builder
public Account(Long id, String email, String password) {
this.id = id;
this.email = email;
this.password = password;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.umc.umcserver.domain.auth.service;

import com.umc.umcserver.domain.auth.dto.LoginRequestDto;
import com.umc.umcserver.global.jwt.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Service
public class AuthService {
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final JwtTokenProvider tokenProvider;

@Transactional
public String login(LoginRequestDto requestDto) {
// Login ID/PW 를 기반으로 AuthenticationToken 생성
UsernamePasswordAuthenticationToken authenticationToken = requestDto.toAuthentication();

// AuthenticationToken (유저 정보: 비밀번호) 검증
// authenticate 메서드가 실행이 될 때 CustomUserDetailsService 에서 만들었던 loadUserByUsername 메서드가 실행됨
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

// 인증 정보를 기반으로 JWT 토큰 생성
String token = tokenProvider.generateToken(authentication);

// 토큰 발급
return token;
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.umc.umcserver.controller;
package com.umc.umcserver.domain.board.controller;

import com.umc.umcserver.dto.*;
import com.umc.umcserver.service.BoardService;
import com.umc.umcserver.domain.board.dto.*;
import com.umc.umcserver.domain.board.service.BoardService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.umc.umcserver.controller;
package com.umc.umcserver.domain.board.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.umc.umcserver.dto;
package com.umc.umcserver.domain.board.dto;

import com.umc.umcserver.repository.Board;
import com.umc.umcserver.domain.board.repository.Board;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.umc.umcserver.dto;
package com.umc.umcserver.domain.board.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.umc.umcserver.dto;
package com.umc.umcserver.domain.board.dto;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.umc.umcserver.dto;
package com.umc.umcserver.domain.board.dto;

import lombok.Getter;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.umc.umcserver.dto;
package com.umc.umcserver.domain.board.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package com.umc.umcserver.dto;
package com.umc.umcserver.domain.board.dto;

import com.umc.umcserver.repository.Board;
import com.umc.umcserver.domain.board.repository.Board;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.data.domain.Pageable;

import java.util.List;

@Getter
@AllArgsConstructor
public class FetchPostsResDto {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.umc.umcserver.dto;
package com.umc.umcserver.domain.board.dto;

import com.umc.umcserver.repository.Board;
import com.umc.umcserver.domain.board.repository.Board;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.umc.umcserver.dto;
package com.umc.umcserver.domain.board.dto;

import com.umc.umcserver.repository.Board;
import com.umc.umcserver.domain.board.repository.Board;
import lombok.AllArgsConstructor;
import lombok.Getter;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.umc.umcserver.repository;
package com.umc.umcserver.domain.board.repository;

import lombok.Builder;
import lombok.Getter;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.umc.umcserver.repository;
package com.umc.umcserver.domain.board.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.umc.umcserver.service;
package com.umc.umcserver.domain.board.service;

import com.umc.umcserver.dto.*;
import com.umc.umcserver.repository.Board;
import com.umc.umcserver.repository.BoardRepository;
import com.umc.umcserver.domain.board.dto.*;
import com.umc.umcserver.domain.board.repository.Board;
import com.umc.umcserver.domain.board.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/umc/umcserver/global/dto/DtoMetaData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.umc.umcserver.global.dto;

import lombok.Data;

@Data
public class DtoMetaData {
private String message;
private String exception;

public DtoMetaData(String message) {
this.message = message;
this.exception = null;
}

public DtoMetaData(String message, String exception) {
this.message = message;
this.exception = exception;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.umc.umcserver.global.jwt;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
// 유저 정보는 있지만 자원에 접근할 수 있는 권한이 없는 경우 403 에러 응답
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.umc.umcserver.global.jwt;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
// 유요한 자격증명(유저 정보) 없이 접근하려 할 때 401 에러 응답
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
48 changes: 48 additions & 0 deletions src/main/java/com/umc/umcserver/global/jwt/JwtCustomFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.umc.umcserver.global.jwt;

import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RequiredArgsConstructor
public class JwtCustomFilter extends OncePerRequestFilter {

public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String BEARER_PREFIX = "Bearer ";

private final JwtTokenProvider tokenProvider;

// 실제 필터링 로직: JWT 토큰의 인증 정보를 현재 쓰레드의 SecurityContext 에 저장
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// Request Header 에서 토큰 꺼냄
String token = resolveToken(request);
// 토큰 유효성 검사
// 유요한 토큰이면 Authentication 을 가져와서 SecurityContext 에 저장
if(StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}

filterChain.doFilter(request, response);
}

// Request Header 에서 토큰 꺼내기
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);

if(StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
return bearerToken.substring(7);
}

return null;
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/umc/umcserver/global/jwt/JwtSecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.umc.umcserver.global.jwt;

import lombok.RequiredArgsConstructor;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@RequiredArgsConstructor
public class JwtSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

private final JwtTokenProvider jwtTokenProvider;

// TokenProvider 를 주입받은 JwtFilter 를 Security Filter 앞에 추가
@Override
public void configure(HttpSecurity http) {
JwtCustomFilter customJwtFilter = new JwtCustomFilter(jwtTokenProvider);
http.addFilterBefore(customJwtFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Loading

0 comments on commit cfc7449

Please sign in to comment.