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

8주차 미션 / 서버 2조 정상현 #19

Open
wants to merge 4 commits into
base: main
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
7 changes: 4 additions & 3 deletions src/main/java/com/kuit/kuit4serverauth/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@

@Configuration
public class WebConfig implements WebMvcConfigurer {
private final AuthInterceptor authInterceptor;
private final AuthInterceptor authInterceptor; // 등록 해야 함

public WebConfig(AuthInterceptor authInterceptor) {
this.authInterceptor = authInterceptor;
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
// TODO /profile, /admin 앞에 붙이기
// /profile, /admin 경로에서 동작하도록 경로제한
registry.addInterceptor(authInterceptor).addPathPatterns("/profile", "/admin");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.HashMap;
import java.util.Map;

// RestController : JSON형식의 응답반환
@RestController
public class AuthController {
private final UserRepository userRepository;
Expand All @@ -24,19 +25,26 @@ public AuthController(UserRepository userRepository, JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}

// @RequestBody 부분 DTO로 감싸기
// API를 통해 JWT 토큰을 발급
@PostMapping("/login")
public ResponseEntity<Map<String, String>> login(@RequestBody Map<String, String> credentials) {
String username = credentials.get("username");
String password = credentials.get("password");

User user = userRepository.findByUsername(username);
// 유저가 있는지, 비밀번호가 맞는지
if (user == null || !user.getPassword().equals(password)) {
throw new CustomException(ErrorCode.INVALID_USERNAME_OR_PASSWORD);
}

// Map을 DTO로 감싸는게 좋다
// JWT Token 생성
String token = jwtUtil.generateToken(user.getUsername(), user.getRole());

// 응답 반환
Map<String, String> response = new HashMap<>();
response.put("token", token);
response.put("token", token); // 토큰을 응답으로 보낸다
return ResponseEntity.ok(response);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.kuit.kuit4serverauth.controller;

import com.kuit.kuit4serverauth.exception.CustomException;
import com.kuit.kuit4serverauth.exception.ErrorCode;
import com.kuit.kuit4serverauth.service.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -9,15 +12,42 @@
@RestController
public class UserController {

private final JwtUtil jwtUtil;

public UserController(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}

// API를 인증된 사용자만 접근 가능하도록
@GetMapping("/profile")
public ResponseEntity<String> getProfile(HttpServletRequest request) {
// TODO : 로그인 한 사용자면 username 이용해 "Hello, {username}" 반환하기
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized");
String token = request.getHeader("Authorization");

if (token == null) {
throw new CustomException(ErrorCode.INVALID_TOKEN);
}

String username = jwtUtil.getUsernameFromToken(token);
return ResponseEntity.ok("Hello, " + username);
}

// API에 관리자 권한 (ROLE_ADMIN) 있는 사용자만 접근 가능하도록
@GetMapping("/admin")
public ResponseEntity<String> getAdmin(HttpServletRequest request) {
// TODO: role이 admin이면 "Hello, admin" 반환하기
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Forbidden");
String token = request.getHeader("Authorization");

if (token == null) {
throw new CustomException(ErrorCode.INVALID_TOKEN);
}

String role = jwtUtil.getRoleFromToken(token);
if (! "ROLE_ADMIN".equals(role)) {
throw new CustomException(ErrorCode.FORBIDDEN_ACCESS);
}

return ResponseEntity.ok("Hello, admin");

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

// HTTP 요청 처리 전 특정 검증 로직 수행
// JWT 검증 + 사용자 정보를 요청 속성에 추가
@Component
public class AuthInterceptor implements HandlerInterceptor {
private final JwtUtil jwtUtil;
Expand All @@ -20,13 +22,18 @@ public AuthInterceptor(JwtUtil jwtUtil) {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String authHeader = request.getHeader("Authorization");

if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
Claims claims = jwtUtil.validateToken(token);

request.setAttribute("username", claims.getSubject());
request.setAttribute("role", claims.get("role"));

// userName / role을 request에 담아서 controller에 보냄
return true;
}

throw new CustomException(ErrorCode.MISSING_AUTH_HEADER);
}
}
Expand Down
26 changes: 22 additions & 4 deletions src/main/java/com/kuit/kuit4serverauth/service/JwtUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,51 @@
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Date;

// JWT를 생성하고 검증
@Component
public class JwtUtil {
private final String secret = "mysecretkey";
private final long expirationMs = 3600000; // 1 hour
// todo
// private final String secret = "mysecretkey";
private final Key secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256); // 안전한 비밀 키 생성
private final long expirationMs = 3600000; // 1 hour, 만료시간
// yml 파일에서 환경변수 설정으로 가져오기 -> 이걸 이용해서 수정

public String generateToken(String username, String role) {
return Jwts.builder()
.setSubject(username)
.claim("role", role)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expirationMs))
.signWith(SignatureAlgorithm.HS256, secret)
.signWith(secretKey)
.compact();
}

// 가져온 토큰을 파싱
public Claims validateToken(String token) {
try {
return Jwts.parser()
.setSigningKey(secret)
.setSigningKey(secretKey) // 발급 한 토큰이 맞는지 검증
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
throw new CustomException(ErrorCode.INVALID_TOKEN);
}
}

// Claims에서 username 추출
public String getUsernameFromToken(String token) {
return validateToken(token).getSubject();
}

// Claims에서 role추출
public String getRoleFromToken(String token) {
return validateToken(token).get("role").toString();
}

}