diff --git a/.gitignore b/.gitignore index 2c7d4de..5a6706d 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,10 @@ out/ ### VS Code ### .vscode/ + +### Credentials ### .env +k8s/secrets.yaml ### Etc ### .DS_Store diff --git a/Dockerfile b/Dockerfile index a2fc474..13b78d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,14 +10,10 @@ ARG GITHUB_CLIENT_ID ARG GITHUB_CLIENT_SECRET ARG JWT_SECRET_KEY -ENV POSTGRES_HOST=${POSTGRES_HOST} -ENV POSTGRES_PORT=${POSTGRES_PORT} -ENV POSTGRES_USER=${POSTGRES_USER} -ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} -ENV POSTGRES_DB=${POSTGRES_DB} -ENV GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID} -ENV GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} -ENV JWT_SECRET_KEY=${JWT_SECRET_KEY} +# Mock environment values for test (related: issue #15) +ENV GITHUB_CLIENT_ID="GITHUB_CLIENT_ID" +ENV GITHUB_CLIENT_SECRET="GITHUB_CLIENT_SECRET" +ENV JWT_SECRET_KEY="CUL0gl15xbD4Y4DFRGCVBkLfXCodzgwOypSL82/HuD4=" WORKDIR /server COPY src /server/src/ diff --git a/docker-compose.yaml b/docker-compose.yaml index 499f728..a5c28bb 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,6 +7,7 @@ services: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} + GITHUB_REDIRECT_URI: ${GITHUB_REDIRECT_URI} GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID} GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET} JWT_SECRET_KEY: ${JWT_SECRET_KEY} @@ -18,9 +19,9 @@ services: postgres: image: postgres:latest environment: - POSTGRES_USER: co-co-gong - POSTGRES_PASSWORD: mypassword - POSTGRES_DB: main + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} PGDATA: /var/lib/postgresql/data/pgdata ports: - "5432:5432" diff --git a/k8s/backend.yaml b/k8s/backend.yaml new file mode 100644 index 0000000..87180ca --- /dev/null +++ b/k8s/backend.yaml @@ -0,0 +1,100 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend +spec: + replicas: 1 + selector: + matchLabels: + app: backend + template: + metadata: + labels: + app: backend + spec: + containers: + - name: backend + image: zerohertzkr/dev:latest + env: + - name: POSTGRES_HOST + value: "postgres" + - name: POSTGRES_PORT + valueFrom: + configMapKeyRef: + name: postgres-config + key: POSTGRES_PORT + - name: POSTGRES_USER + valueFrom: + configMapKeyRef: + name: postgres-config + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: postgres-secret + key: POSTGRES_PASSWORD + - name: POSTGRES_DB + valueFrom: + configMapKeyRef: + name: postgres-config + key: POSTGRES_DB + - name: GITHUB_REDIRECT_URI + valueFrom: + secretKeyRef: + name: oauth-secret + key: GITHUB_REDIRECT_URI + - name: GITHUB_CLIENT_ID + valueFrom: + secretKeyRef: + name: oauth-secret + key: GITHUB_CLIENT_ID + - name: GITHUB_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: oauth-secret + key: GITHUB_CLIENT_SECRET + - name: JWT_SECRET_KEY + valueFrom: + secretKeyRef: + name: jwt-secret + key: JWT_SECRET_KEY + command: + - "scripts/local.sh" + # command: + # - "sh" + # - "-c" + # - "tail -f /dev/null" + volumeMounts: + - mountPath: /home/zerohertz/workspace + name: backend-storage + volumes: + - name: backend-storage + hostPath: + path: /home/zerohertz/Zerohertz/co-co-gong-server + type: DirectoryOrCreate +--- +apiVersion: v1 +kind: Service +metadata: + name: backend +spec: + ports: + - port: 8080 + selector: + app: backend +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: co-co-gong +spec: + entryPoints: + - websecure + routes: + - match: Host(`co-co-gong.zerohertz.xyz`) + kind: Rule + services: + - name: backend + port: 8080 + tls: + certResolver: zerohertz-resolver diff --git a/k8s/postgresql.yaml b/k8s/postgresql.yaml new file mode 100644 index 0000000..634b7e5 --- /dev/null +++ b/k8s/postgresql.yaml @@ -0,0 +1,47 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:latest + env: + - name: POSTGRES_DB + valueFrom: + configMapKeyRef: + name: postgres-config + key: POSTGRES_DB + - name: POSTGRES_USER + valueFrom: + configMapKeyRef: + name: postgres-config + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: postgres-secret + key: POSTGRES_PASSWORD + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + ports: + - containerPort: 5432 +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres +spec: + ports: + - port: 5432 + selector: + app: postgres diff --git a/scripts/compose.sh b/scripts/compose.sh index 82f67f4..3cb8da1 100755 --- a/scripts/compose.sh +++ b/scripts/compose.sh @@ -1,20 +1,12 @@ #!/bin/bash source .env -export JWT_SECRET_KEY=$(openssl rand -base64 32) +export JWT_SECRET_KEY=$(openssl rand -base64 64) docker compose down -v docker rmi co-co-gong-server-server --force -if docker compose build --no-cache \ - --build-arg POSTGRES_HOST=${POSTGRES_HOST} \ - --build-arg POSTGRES_PORT=${POSTGRES_PORT} \ - --build-arg POSTGRES_USER=${POSTGRES_USER} \ - --build-arg POSTGRES_PASSWORD=${POSTGRES_PASSWORD} \ - --build-arg POSTGRES_DB=${POSTGRES_DB} \ - --build-arg GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID} \ - --build-arg GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} \ - --build-arg JWT_SECRET_KEY=${JWT_SECRET_KEY}; then +if docker compose build --no-cache; then echo "Build success!" else echo "Build failed..." diff --git a/scripts/docker.sh b/scripts/docker.sh index 9442b46..ba8f33f 100755 --- a/scripts/docker.sh +++ b/scripts/docker.sh @@ -1,15 +1,4 @@ #!/bin/bash -source .env -export JWT_SECRET_KEY=$(openssl rand -base64 32) - docker build --no-cache \ - --build-arg POSTGRES_HOST=${POSTGRES_HOST} \ - --build-arg POSTGRES_PORT=${POSTGRES_PORT} \ - --build-arg POSTGRES_USER=${POSTGRES_USER} \ - --build-arg POSTGRES_PASSWORD=${POSTGRES_PASSWORD} \ - --build-arg POSTGRES_DB=${POSTGRES_DB} \ - --build-arg GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID} \ - --build-arg GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} \ - --build-arg JWT_SECRET_KEY=${JWT_SECRET_KEY} \ -t test . diff --git a/scripts/k8s.sh b/scripts/k8s.sh new file mode 100755 index 0000000..7d97505 --- /dev/null +++ b/scripts/k8s.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +kubectl delete ns co-co-gong +kubectl create ns co-co-gong +kubectl apply -n co-co-gong -f k8s + +# kubectl exec -it -n co-co-gong deploy/backend -- zsh diff --git a/scripts/local.sh b/scripts/local.sh new file mode 100755 index 0000000..8bff8f1 --- /dev/null +++ b/scripts/local.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +./gradlew build +java -jar build/libs/*SNAPSHOT.jar diff --git a/src/main/java/com/server/domain/oauth/controller/OAuthLoginController.java b/src/main/java/com/server/domain/oauth/controller/OAuthLoginController.java index 784685a..c65b96c 100644 --- a/src/main/java/com/server/domain/oauth/controller/OAuthLoginController.java +++ b/src/main/java/com/server/domain/oauth/controller/OAuthLoginController.java @@ -1,5 +1,22 @@ package com.server.domain.oauth.controller; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; import com.server.domain.oauth.dto.GithubDto; import com.server.domain.oauth.dto.OAuthInfo; @@ -8,14 +25,9 @@ import com.server.global.dto.ApiResponseDto; import com.server.global.dto.TokenDto; import com.server.global.jwt.JwtService; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.client.RestTemplate; @RestController @Slf4j @@ -25,6 +37,8 @@ public class OAuthLoginController { private final UserService userService; private final JwtService jwtService; + @Value("${spring.security.oauth2.client.registration.github.redirect-uri}") + private String redirectUri; @Value("${spring.security.oauth2.client.registration.github.client-id}") private String clientId; @Value("${spring.security.oauth2.client.registration.github.client-secret}") @@ -33,16 +47,13 @@ public class OAuthLoginController { // 새로 추가된 로그인 시작점 @GetMapping("/login") public ResponseEntity login() { - String githubAuthUrl = "https://github.com/login/oauth/authorize" + - "?client_id=" + clientId + - "&redirect_uri=http://localhost:8080/login/oauth2/code/github"; + String githubAuthUrl = String.format("%s%s%s%s%s", "https://github.com/login/oauth/authorize?client_id=", + clientId, "&redirect_uri=", redirectUri, "/login/oauth2/code/github"); return ResponseEntity.status(HttpStatus.FOUND) .header(HttpHeaders.LOCATION, githubAuthUrl) .build(); } - - @GetMapping("/login/oauth2/code/github") public ResponseEntity githubLogin(@RequestParam String code) { try { @@ -53,8 +64,7 @@ public ResponseEntity githubLogin(@RequestParam String code) { "https://github.com/login/oauth/access_token", HttpMethod.POST, getAccessToken(code), - OAuthInfo.class - ); + OAuthInfo.class); String githubAccessToken = response.getBody().getAccessToken(); // GitHub 사용자 정보 요청 @@ -62,8 +72,7 @@ public ResponseEntity githubLogin(@RequestParam String code) { "https://api.github.com/user", HttpMethod.GET, getUserInfo(githubAccessToken), - GithubDto.class - ); + GithubDto.class); GithubDto githubDto = userInfoResponse.getBody(); log.info("Received user info. Username: " + githubDto.getUsername()); @@ -97,14 +106,16 @@ public ResponseEntity refreshToken(@RequestBody TokenDto tokenDto) { String refreshToken = tokenDto.getRefreshToken(); if (jwtService.validateToken(refreshToken)) { - String userName = jwtService.getUserNameFromToken(refreshToken); - User user = userService.findByUserName(userName); - - if (user != null && refreshToken.equals(user.getRefreshToken())) { - String newAccessToken = jwtService.createAccessToken(userName); - TokenDto newTokenDto = new TokenDto(newAccessToken, refreshToken); - return ResponseEntity.ok(ApiResponseDto.success(newTokenDto)); + String userName = jwtService.extractUserName(refreshToken).get(); + Optional user = userService.findByUserName(userName); + if (user.isPresent()) { + if (refreshToken.equals(user.get().getRefreshToken())) { + String newAccessToken = jwtService.createAccessToken(userName); + TokenDto newTokenDto = new TokenDto(newAccessToken, refreshToken); + return ResponseEntity.ok(ApiResponseDto.success(newTokenDto)); + } } + } return ResponseEntity.status(HttpStatus.UNAUTHORIZED) @@ -128,4 +139,4 @@ private HttpEntity> getUserInfo(String accessToken headers.setBearerAuth(accessToken); return new HttpEntity<>(headers); } -} \ No newline at end of file +} diff --git a/src/main/java/com/server/domain/user/controller/UserController.java b/src/main/java/com/server/domain/user/controller/UserController.java index 771eb36..d46cfab 100644 --- a/src/main/java/com/server/domain/user/controller/UserController.java +++ b/src/main/java/com/server/domain/user/controller/UserController.java @@ -1,60 +1,95 @@ -// package com.server.domain.user.controller; -// -// import java.util.Optional; -// -// import org.springframework.beans.factory.annotation.Autowired; -// import org.springframework.http.ResponseEntity; -// import org.springframework.web.bind.annotation.DeleteMapping; -// import org.springframework.web.bind.annotation.GetMapping; -// import org.springframework.web.bind.annotation.PathVariable; -// import org.springframework.web.bind.annotation.PostMapping; -// import org.springframework.web.bind.annotation.PutMapping; -// import org.springframework.web.bind.annotation.RequestBody; -// import org.springframework.web.bind.annotation.RequestMapping; -// import org.springframework.web.bind.annotation.RestController; -// -// import com.server.domain.user.entity.User; -// import com.server.domain.user.service.UserService; -// -// @RestController -// @RequestMapping("/api/users") -// public class UserController { -// -// @Autowired -// private UserService userService; -// -// @GetMapping("/{username}") -// public ResponseEntity getUserByUsername(@PathVariable String username) -// { -// Optional user = userService.getUserByUsername(username); -// return user.map(ResponseEntity::ok).orElseGet(() -> -// ResponseEntity.notFound().build()); -// } -// -// @PostMapping -// public ResponseEntity createUser(@RequestBody User user) { -// user.setCreatedAt(java.time.LocalDateTime.now()); -// User createdUser = userService.createUser(user); -// return ResponseEntity.ok(createdUser); -// } -// -// @PutMapping("/{username}") -// public ResponseEntity updateUser(@PathVariable String username, -// @RequestBody User user) { -// Optional existingUser = userService.getUserByUsername(username); -// if (existingUser.isPresent()) { -// user.setUsername(username); -// user.setCreatedAt(existingUser.get().getCreatedAt()); -// User updatedUser = userService.updateUser(user); -// return ResponseEntity.ok(updatedUser); -// } else { -// return ResponseEntity.notFound().build(); -// } -// } -// -// @DeleteMapping("/{username}") -// public ResponseEntity deleteUser(@PathVariable String username) { -// userService.deleteUser(username); -// return ResponseEntity.noContent().build(); -// } -// } + package com.server.domain.user.controller; + + import java.util.Optional; + + import com.server.global.jwt.JwtService; + import jakarta.servlet.http.HttpServletRequest; + import lombok.RequiredArgsConstructor; + import lombok.extern.slf4j.Slf4j; + import org.springframework.http.HttpStatus; + import org.springframework.http.ResponseEntity; + import org.springframework.web.bind.annotation.DeleteMapping; + import org.springframework.web.bind.annotation.GetMapping; + import org.springframework.web.bind.annotation.PathVariable; + import org.springframework.web.bind.annotation.PutMapping; + import org.springframework.web.bind.annotation.RequestMapping; + import org.springframework.web.bind.annotation.RestController; + + import com.server.domain.user.entity.User; + import com.server.domain.user.service.UserService; + + @RestController + @RequiredArgsConstructor + @Slf4j + @RequestMapping("/api/users") + public class UserController { + + private final JwtService jwtService; + private final UserService userService; + + //내 정보 + @GetMapping("/me") + public ResponseEntity getUser(HttpServletRequest request) { + // 디버깅을 위한 로그 추가 + log.info("Received request to /me endpoint"); + + // request(token)에서 username 추출 + Optional username = jwtService.extractUserNameFromToken(request); + if (username.isEmpty()) { + log.warn("Failed to extract username from token"); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid or missing token"); + } + + log.info("Extracted username: {}", username.get()); + + // username으로 찾은 user 반환 + Optional user = userService.findByUserName(username.get()); + + return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); + } + + + @GetMapping("/{username}") + public ResponseEntity getUserByUsername(@PathVariable String username) + { + Optional user = userService.findByUserName(username); + return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); + } + + + + //정보 수정 + //현재 바꿀 수 있는 거 email. 추후 닉네임 추가..? + @PutMapping + public ResponseEntity updateUser(HttpServletRequest request, String email) { + Optional username = jwtService.extractUserNameFromToken(request); + if (username.isEmpty()) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid or missing token"); + } + + // username으로 찾은 user 반환 + Optional user = userService.findByUserName(username.get()); + + return user.map(value ->{ + User changedUser = userService.updateEmail(value, email); + return ResponseEntity.ok(changedUser.getEmail()); + }).orElseGet(()-> ResponseEntity.notFound().build()); + + } + + //사용자 탈퇴 + @DeleteMapping("/me") + public ResponseEntity deleteUser(HttpServletRequest request){ + Optional username = jwtService.extractUserNameFromToken(request); + if (username.isEmpty()) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid or missing token"); + } + + // username으로 찾은 user 반환 + Optional user = userService.findByUserName(username.get()); + return user.map(value ->{ + userService.deleteUser(value); + return ResponseEntity.ok("Success delete user "+value.getUsername()); + }).orElseGet(()->ResponseEntity.notFound().build()); + } + } diff --git a/src/main/java/com/server/domain/user/entity/User.java b/src/main/java/com/server/domain/user/entity/User.java index 7a0bdb4..afbd0eb 100644 --- a/src/main/java/com/server/domain/user/entity/User.java +++ b/src/main/java/com/server/domain/user/entity/User.java @@ -62,4 +62,8 @@ public User(String username, String thumbnail, String email, String oauth, Strin public void updateRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } + + public void updateEmail(String email){ + this.email = email; + } } diff --git a/src/main/java/com/server/domain/user/service/UserService.java b/src/main/java/com/server/domain/user/service/UserService.java index 41d02d4..07e853c 100644 --- a/src/main/java/com/server/domain/user/service/UserService.java +++ b/src/main/java/com/server/domain/user/service/UserService.java @@ -9,6 +9,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + @Service @RequiredArgsConstructor public class UserService { @@ -42,9 +44,17 @@ public void saveRefreshToken(String userName, String refreshToken) { userRepository.save(user); } - public User findByUserName(String userName) { - return userRepository.findByUsername(userName) - .orElseThrow(() -> new RuntimeException("User not found")); + public Optional findByUserName(String userName) { + return userRepository.findByUsername(userName); + } + + public User updateEmail(User user, String email) { + user.updateEmail(email); + return userRepository.save(user); + } + public void deleteUser(User user) { + userRepository.delete(user); + } } \ No newline at end of file diff --git a/src/main/java/com/server/global/config/SwaggerConfig.java b/src/main/java/com/server/global/config/SwaggerConfig.java index 30ac614..35a1df3 100644 --- a/src/main/java/com/server/global/config/SwaggerConfig.java +++ b/src/main/java/com/server/global/config/SwaggerConfig.java @@ -1,5 +1,7 @@ package com.server.global.config; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -9,12 +11,19 @@ @Configuration public class SwaggerConfig { - + //authorize 버튼 추가 @Bean public OpenAPI openAPI() { return new OpenAPI() - .components(new Components()) - .info(apiInfo()); + .components(new Components() + .addSecuritySchemes("bearerAuth", new SecurityScheme() + .name("bearerAuth") + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + )) + .info(apiInfo()) + .addSecurityItem(new SecurityRequirement().addList("bearerAuth")); } private Info apiInfo() { @@ -23,4 +32,4 @@ private Info apiInfo() { .description("CoCoGong Server API Spec") .version("0.0.1"); } -} +} \ No newline at end of file diff --git a/src/main/java/com/server/global/jwt/JwtService.java b/src/main/java/com/server/global/jwt/JwtService.java index e1b45ee..e8178de 100644 --- a/src/main/java/com/server/global/jwt/JwtService.java +++ b/src/main/java/com/server/global/jwt/JwtService.java @@ -1,17 +1,19 @@ package com.server.global.jwt; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; import java.util.Date; +import java.util.Optional; @Service +@Slf4j public class JwtService { @Value("${jwt.secret}") private String secret; @@ -22,6 +24,9 @@ public class JwtService { @Value("${jwt.refresh-token-expiration}") private long refreshTokenExpiration; + private static final String USERNAME_CLAIM = "userName"; + private static final String BEARER = "Bearer "; + private SecretKey getSigningKey() { byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8); return Keys.hmacShaKeyFor(keyBytes); @@ -36,34 +41,77 @@ public String createRefreshToken(String userName) { } private String createToken(String userName, long expiration) { - Claims claims = Jwts.claims().setSubject(userName); Date now = new Date(); Date expiryDate = new Date(now.getTime() + expiration); - return Jwts.builder() - .setClaims(claims) + String token = Jwts.builder() + .setSubject(userName) + .claim(USERNAME_CLAIM, userName) // 명시적으로 USERNAME_CLAIM 추가 .setIssuedAt(now) .setExpiration(expiryDate) .signWith(getSigningKey(), SignatureAlgorithm.HS512) .compact(); + + log.info("Created token for user: {}, token: {}", userName, token); + return token; } public boolean validateToken(String token) { try { Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token); + log.info("Token is valid"); return true; } catch (Exception e) { + log.error("Invalid JWT token: {}", e.getMessage()); return false; } } - public String getUserNameFromToken(String token) { - Claims claims = Jwts.parserBuilder() - .setSigningKey(getSigningKey()) - .build() - .parseClaimsJws(token) - .getBody(); + public Optional extractUserName(String token) { + try { + Claims claims = Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + + String userName = claims.get(USERNAME_CLAIM, String.class); + if (userName == null) { + userName = claims.getSubject(); // USERNAME_CLAIM이 없으면 subject를 사용 + } + log.info("Extracted username from token: {}", userName); + return Optional.ofNullable(userName); + } catch (Exception e) { + log.error("Failed to extract username from token: {}", e.getMessage()); + return Optional.empty(); + } + } + + public Optional extractAccessToken(HttpServletRequest request) { + String authHeader = request.getHeader("Authorization"); + log.info("Authorization header: {}", authHeader); + + return Optional.ofNullable(authHeader) + .filter(accessToken -> accessToken.startsWith(BEARER)) + .map(accessToken -> { + String token = accessToken.substring(BEARER.length()).trim(); + log.info("Extracted access token: {}", token); + return token; + }); + } + + public Optional extractUserNameFromToken(HttpServletRequest request) { + Optional accessToken = extractAccessToken(request); + if (accessToken.isEmpty()) { + log.warn("Access token is empty"); + return Optional.empty(); + } + + if (!validateToken(accessToken.get())) { + log.warn("Token is invalid"); + return Optional.empty(); + } - return claims.getSubject(); + return extractUserName(accessToken.get()); } } \ No newline at end of file diff --git a/src/main/resources/application-deploy.yaml b/src/main/resources/application-deploy.yaml index 39d3276..0b3cbc1 100644 --- a/src/main/resources/application-deploy.yaml +++ b/src/main/resources/application-deploy.yaml @@ -12,5 +12,6 @@ spring: client: registration: github: + redirect-uri: ${GITHUB_REDIRECT_URI} client-id: ${GITHUB_CLIENT_ID} client-secret: ${GITHUB_CLIENT_SECRET} diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml index 3756842..75ba226 100644 --- a/src/main/resources/application-local.yaml +++ b/src/main/resources/application-local.yaml @@ -11,6 +11,7 @@ spring: client: registration: github: + redirect-uri: "http://localhost:8080" client-id: ${GITHUB_CLIENT_ID} client-secret: ${GITHUB_CLIENT_SECRET} diff --git a/src/main/resources/application-test.yaml b/src/main/resources/application-test.yaml index 3c8f604..03acb6b 100644 --- a/src/main/resources/application-test.yaml +++ b/src/main/resources/application-test.yaml @@ -13,5 +13,6 @@ spring: client: registration: github: + redirect-uri: "http://localhost:8080" client-id: secret client-secret: secret