diff --git a/choseoyun/likelion-kakao-login/.idea/.gitignore b/choseoyun/likelion-kakao-login/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/choseoyun/likelion-kakao-login/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/choseoyun/likelion-kakao-login/.idea/compiler.xml b/choseoyun/likelion-kakao-login/.idea/compiler.xml new file mode 100644 index 0000000..107242e --- /dev/null +++ b/choseoyun/likelion-kakao-login/.idea/compiler.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/choseoyun/likelion-kakao-login/.idea/dataSources.xml b/choseoyun/likelion-kakao-login/.idea/dataSources.xml new file mode 100644 index 0000000..fc11216 --- /dev/null +++ b/choseoyun/likelion-kakao-login/.idea/dataSources.xml @@ -0,0 +1,29 @@ + + + + + h2.unified + true + org.h2.Driver + jdbc:h2:tcp://localhost:909280/default + + + + + + $ProjectFileDir$ + + + h2.unified + true + org.h2.Driver + jdbc:h2:tcp://localhost/~/test + + + + + + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/choseoyun/likelion-kakao-login/.idea/gradle.xml b/choseoyun/likelion-kakao-login/.idea/gradle.xml new file mode 100644 index 0000000..3a363e2 --- /dev/null +++ b/choseoyun/likelion-kakao-login/.idea/gradle.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/choseoyun/likelion-kakao-login/.idea/jarRepositories.xml b/choseoyun/likelion-kakao-login/.idea/jarRepositories.xml new file mode 100644 index 0000000..fdc392f --- /dev/null +++ b/choseoyun/likelion-kakao-login/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/choseoyun/likelion-kakao-login/.idea/likelion-kakao-login.iml b/choseoyun/likelion-kakao-login/.idea/likelion-kakao-login.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/choseoyun/likelion-kakao-login/.idea/likelion-kakao-login.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/choseoyun/likelion-kakao-login/.idea/misc.xml b/choseoyun/likelion-kakao-login/.idea/misc.xml new file mode 100644 index 0000000..1c03060 --- /dev/null +++ b/choseoyun/likelion-kakao-login/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/choseoyun/likelion-kakao-login/.idea/modules.xml b/choseoyun/likelion-kakao-login/.idea/modules.xml new file mode 100644 index 0000000..0276fde --- /dev/null +++ b/choseoyun/likelion-kakao-login/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/.gitattributes b/choseoyun/likelion-kakao-login/likelion-kakao-login/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/.gitignore b/choseoyun/likelion-kakao-login/likelion-kakao-login/.gitignore new file mode 100644 index 0000000..9289965 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/.gitignore @@ -0,0 +1,39 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +src/**/application-prod.yml diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/build.gradle b/choseoyun/likelion-kakao-login/likelion-kakao-login/build.gradle new file mode 100644 index 0000000..428fbf4 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/build.gradle @@ -0,0 +1,51 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.3.5' + id 'io.spring.dependency-management' version '1.1.6' + id 'org.cyclonedx.bom' version '1.10.0' +} + +group = 'org.likelion' +version = '0.0.1-SNAPSHOT' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-webflux' + compileOnly 'org.projectlombok:lombok' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + runtimeOnly 'com.h2database:h2' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.projectreactor:reactor-test' + testImplementation 'org.springframework.security:spring-security-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + //Webflux + implementation 'org.springframework.boot:spring-boot-starter-webflux' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/gradle/wrapper/gradle-wrapper.jar b/choseoyun/likelion-kakao-login/likelion-kakao-login/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/choseoyun/likelion-kakao-login/likelion-kakao-login/gradle/wrapper/gradle-wrapper.jar differ diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/gradle/wrapper/gradle-wrapper.properties b/choseoyun/likelion-kakao-login/likelion-kakao-login/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..df97d72 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/gradlew b/choseoyun/likelion-kakao-login/likelion-kakao-login/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/gradlew.bat b/choseoyun/likelion-kakao-login/likelion-kakao-login/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/settings.gradle b/choseoyun/likelion-kakao-login/likelion-kakao-login/settings.gradle new file mode 100644 index 0000000..f8cb7b9 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'likelion-kakao-login' diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/JwtFilter.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/JwtFilter.java new file mode 100644 index 0000000..256a8bd --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/JwtFilter.java @@ -0,0 +1,33 @@ +package org.likelion.likelionkakaologin; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +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.GenericFilterBean; + +import java.io.IOException; + +@RequiredArgsConstructor +public class JwtFilter extends GenericFilterBean { + private final TokenProvider tokenProvider; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + String jwt = tokenProvider.resolveToken((HttpServletRequest) request); + + if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) { + Authentication authentication = tokenProvider.getAuthentication(jwt); + + // SecurityContext에 Authentication 객체를 저장 (인증 정보(authentication)를 Spring Security에게 넘김) + SecurityContextHolder.getContext().setAuthentication(authentication); + + } + chain.doFilter(request, response); + } +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoLoginController.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoLoginController.java new file mode 100644 index 0000000..d443fe0 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoLoginController.java @@ -0,0 +1,21 @@ +package org.likelion.likelionkakaologin; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/callback") +public class KakaoLoginController { + + private final KakaoService kakaoService; + + // 로그인 및 회원가입 + @GetMapping + public @ResponseBody Token kakaoLogin(@RequestParam("code") String code) { + String kakaoAccessToken = kakaoService.getAccessToken(code); + Token token = kakaoService.loginOrSignUp(kakaoAccessToken); + System.out.println("로그인 성공 !"); + return token; + } +} \ No newline at end of file diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoService.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoService.java new file mode 100644 index 0000000..64fef2b --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoService.java @@ -0,0 +1,84 @@ +package org.likelion.likelionkakaologin; + +import io.netty.handler.codec.http.HttpHeaderValues; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.likelion.likelionkakaologin.user.Role; +import org.likelion.likelionkakaologin.user.User; +import org.likelion.likelionkakaologin.user.UserRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.reactive.function.client.WebClient; + +@Slf4j +@RequiredArgsConstructor +@Service +public class KakaoService { + + @Value("${kakao.client_id}") // 임포트 lombok(X), spring(O) + private String clientId; + + private final String KAUTH_TOKEN_URL_HOST = "https://kauth.kakao.com"; // 액세스 토큰을 발급받기 위한 서버 + private final String KAUTH_USER_URL_HOST = "https://kapi.kakao.com"; // 사용자 정보를 받아오기 위한 서버 + private final UserRepository userRepository; + private final TokenProvider tokenProvider; + + // 인가코드를 이용해 액세스 토큰 받아오기 + public String getAccessToken(String code) { + + KakaoTokenResDto kakaoTokenResDto = WebClient.create(KAUTH_TOKEN_URL_HOST).post()// 카카오 인증 서버로 post 요청 준비 + .uri(uriBuilder -> uriBuilder + .scheme("https") + .path("/oauth/token") + .queryParam("grant_type", "authorization_code") + .queryParam("client_id", clientId) + .queryParam("code", code) + .build(true)) + .header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()) + .retrieve() // 요청을 보내고 응답 받기 + .bodyToMono(KakaoTokenResDto.class) // 카카오 서버로부터 받아온 응답 본문을 우리가 만든 dto로 변환 + .block(); // 비동기 방식으로 처리된 결과를 동기적으로 받을 수 있게 + + return kakaoTokenResDto.getAccessToken(); + } + + // 액세스 토큰을 이용해 사용자 정보 가져오기 + public KakaoUserInfo getUserInfo(String accessToken) { + + KakaoUserInfo userInfo = WebClient.create(KAUTH_USER_URL_HOST) + .get() + .uri(uriBuilder -> uriBuilder + .scheme("https") + .path("/v2/user/me") + .build(true)) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) + .header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()) + .retrieve() + .bodyToMono(KakaoUserInfo.class) + .block(); + + return userInfo; + } + + // 사용자 정보로 로그인 구현 + @Transactional + public Token loginOrSignUp(String kakaoAccessToken) { + KakaoUserInfo userInfo = getUserInfo(kakaoAccessToken); + Long id = userInfo.getId(); + + User user = userRepository.findById(id).orElseGet(() -> + userRepository.save(User.builder() + .id(id) + .name(userInfo.getKakaoAccount().getProfile().getNickName()) + .email(userInfo.getKakaoAccount().getEmail()) + .pictureUrl(userInfo.getKakaoAccount().getProfile().getProfileImageUrl()) + .role(Role.ROLE_USER) + .build()) + + ); + + return tokenProvider.createToken(user); + } +} \ No newline at end of file diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoTokenResDto.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoTokenResDto.java new file mode 100644 index 0000000..a293396 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoTokenResDto.java @@ -0,0 +1,27 @@ +package org.likelion.likelionkakaologin; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor //역직렬화를 위한 기본 생성자 +@JsonIgnoreProperties(ignoreUnknown = true) +public class KakaoTokenResDto { + + @JsonProperty("token_type") + public String tokenType; + @JsonProperty("access_token") + public String accessToken; + @JsonProperty("id_token") + public String idToken; + @JsonProperty("expires_in") + public Integer expiresIn; + @JsonProperty("refresh_token") + public String refreshToken; + @JsonProperty("refresh_token_expires_in") + public Integer refreshTokenExpiresIn; + @JsonProperty("scope") + public String scope; +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoUserInfo.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoUserInfo.java new file mode 100644 index 0000000..793f4b3 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/KakaoUserInfo.java @@ -0,0 +1,190 @@ +package org.likelion.likelionkakaologin; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.Date; +import java.util.HashMap; + +@Getter +@NoArgsConstructor //역직렬화를 위한 기본 생성자 +@JsonIgnoreProperties(ignoreUnknown = true) +public class KakaoUserInfo { + + //회원 번호 + @JsonProperty("id") + public Long id; + + //자동 연결 설정을 비활성화한 경우만 존재. + //true : 연결 상태, false : 연결 대기 상태 + @JsonProperty("has_signed_up") + public Boolean hasSignedUp; + + //서비스에 연결 완료된 시각. UTC + @JsonProperty("connected_at") + public Date connectedAt; + + //카카오싱크 간편가입을 통해 로그인한 시각. UTC + @JsonProperty("synched_at") + public Date synchedAt; + + //사용자 프로퍼티 + @JsonProperty("properties") + public HashMap properties; + + //카카오 계정 정보 + @JsonProperty("kakao_account") + public KakaoAccount kakaoAccount; + + //uuid 등 추가 정보 + @JsonProperty("for_partner") + public Partner partner; + + @Getter + @NoArgsConstructor + @JsonIgnoreProperties(ignoreUnknown = true) + public class KakaoAccount { + + //프로필 정보 제공 동의 여부 + @JsonProperty("profile_needs_agreement") + public Boolean isProfileAgree; + + //닉네임 제공 동의 여부 + @JsonProperty("profile_nickname_needs_agreement") + public Boolean isNickNameAgree; + + //프로필 사진 제공 동의 여부 + @JsonProperty("profile_image_needs_agreement") + public Boolean isProfileImageAgree; + + //사용자 프로필 정보 + @JsonProperty("profile") + public Profile profile; + + //이름 제공 동의 여부 + @JsonProperty("name_needs_agreement") + public Boolean isNameAgree; + + //카카오계정 이름 + @JsonProperty("name") + public String name; + + //이메일 제공 동의 여부 + @JsonProperty("email_needs_agreement") + public Boolean isEmailAgree; + + //이메일이 유효 여부 + // true : 유효한 이메일, false : 이메일이 다른 카카오 계정에 사용돼 만료 + @JsonProperty("is_email_valid") + public Boolean isEmailValid; + + //이메일이 인증 여부 + //true : 인증된 이메일, false : 인증되지 않은 이메일 + @JsonProperty("is_email_verified") + public Boolean isEmailVerified; + + //카카오계정 대표 이메일 + @JsonProperty("email") + public String email; + + //연령대 제공 동의 여부 + @JsonProperty("age_range_needs_agreement") + public Boolean isAgeAgree; + + //연령대 + //참고 https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#req-user-info + @JsonProperty("age_range") + public String ageRange; + + //출생 연도 제공 동의 여부 + @JsonProperty("birthyear_needs_agreement") + public Boolean isBirthYearAgree; + + //출생 연도 (YYYY 형식) + @JsonProperty("birthyear") + public String birthYear; + + //생일 제공 동의 여부 + @JsonProperty("birthday_needs_agreement") + public Boolean isBirthDayAgree; + + //생일 (MMDD 형식) + @JsonProperty("birthday") + public String birthDay; + + //생일 타입 + // SOLAR(양력) 혹은 LUNAR(음력) + @JsonProperty("birthday_type") + public String birthDayType; + + //성별 제공 동의 여부 + @JsonProperty("gender_needs_agreement") + public Boolean isGenderAgree; + + //성별 + @JsonProperty("gender") + public String gender; + + //전화번호 제공 동의 여부 + @JsonProperty("phone_number_needs_agreement") + public Boolean isPhoneNumberAgree; + + //전화번호 + //국내 번호인 경우 +82 00-0000-0000 형식 + @JsonProperty("phone_number") + public String phoneNumber; + + //CI 동의 여부 + @JsonProperty("ci_needs_agreement") + public Boolean isCIAgree; + + //CI, 연계 정보 + @JsonProperty("ci") + public String ci; + + //CI 발급 시각, UTC + @JsonProperty("ci_authenticated_at") + public Date ciCreatedAt; + + @Getter + @NoArgsConstructor + @JsonIgnoreProperties(ignoreUnknown = true) + public class Profile { + + //닉네임 + @JsonProperty("nickname") + public String nickName; + + //프로필 미리보기 이미지 URL + @JsonProperty("thumbnail_image_url") + public String thumbnailImageUrl; + + //프로필 사진 URL + @JsonProperty("profile_image_url") + public String profileImageUrl; + + //프로필 사진 URL 기본 프로필인지 여부 + //true : 기본 프로필, false : 사용자 등록 + @JsonProperty("is_default_image") + public String isDefaultImage; + + //닉네임이 기본 닉네임인지 여부 + //true : 기본 닉네임, false : 사용자 등록 + @JsonProperty("is_default_nickname") + public Boolean isDefaultNickName; + + } + } + + @Getter + @NoArgsConstructor + @JsonIgnoreProperties(ignoreUnknown = true) + public class Partner { + //고유 ID + @JsonProperty("uuid") + public String uuid; + } + +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/LikelionKakaoLoginApplication.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/LikelionKakaoLoginApplication.java new file mode 100644 index 0000000..f693161 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/LikelionKakaoLoginApplication.java @@ -0,0 +1,13 @@ +package org.likelion.likelionkakaologin; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LikelionKakaoLoginApplication { + + public static void main(String[] args) { + SpringApplication.run(LikelionKakaoLoginApplication.class, args); + } + +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/SecurityConfig.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/SecurityConfig.java new file mode 100644 index 0000000..41cbae5 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/SecurityConfig.java @@ -0,0 +1,64 @@ +package org.likelion.likelionkakaologin; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; + +import java.util.List; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final TokenProvider tokenProvider; + + // 요청에 대한 인증 및 권한 부여 정의 + // Http 요청에 대해 다양한 보안 필터들을 순차적으로 적용하는 구조 + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + return http + .httpBasic(AbstractHttpConfigurer::disable) // 기본 HTTP 인증 비활성화. 기본 인증은 보안에 취약할 수 있어 주로 비활성화하는 편. + .csrf(AbstractHttpConfigurer::disable) // CSRF 보호 기능 비활성화. 주로 브라우저 기반 클라이언트에서 사용하는 보호 기능이다. 보통 비활성화하는 편. + .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + // 세션 관리 정책(서버측에서 세션 생성 X. 클라이언트에서 jwt 토큰을 사용하여 인증하도록 함) + .formLogin(AbstractHttpConfigurer::disable) // 스프링의 기본 로그인 폼을 사용하지 않고, auth 등의 외부 인증 방식을 사용하기 위해 비활성화. + .logout(AbstractHttpConfigurer::disable) // 로그아웃 기능 비활성화 + .authorizeHttpRequests(authorizeRequests -> authorizeRequests + .requestMatchers("/callback/**").permitAll() // 이 경로에 대해서는 모든 사용자가 접근할 수 있도록 허용 + .anyRequest().authenticated() // 그 외 모든 요청은 인증이 필요하도록 설정 + ) // 인증 및 권한 부여 규칙 설정 + .cors(cors -> cors.configurationSource(configurationSource())) + .addFilterBefore(new JwtFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class) + // jwt 토큰 인증을 위한 jwtfilter 추가 + .build(); + } + + // CORS 설정을 정의하는 메서드. + // CORS는 다른 도메인에서 리소스를 요청할 때 이를 허용할 지 여부를 결정하는 매커니즘 + @Bean + public CorsConfigurationSource configurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + + configuration.setAllowedOriginPatterns(List.of("*")); // 모든 도메인에서의 요청 허용 + configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")); // HTTP 에 대한 요청 허용 + configuration.setAllowedHeaders(List.of("*")); // 모든 헤더 요청 허용 + configuration.setExposedHeaders(List.of("Access-Control-Allow-Credentials", "Authorization", "Set-Cookie")); // 특정 응답 헤더를 클라이언트가 접근할 수 있도록 노출 + configuration.setAllowCredentials(true); // 자격 증명(쿠기 등)을 포함한 요청 허용 + configuration.setMaxAge(3600L); // 브라우저가 사전 요청을 캐시할 시간 1시간으로 설정 + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); // 모든 경로에 대해 CORS 설정 적용 + + return source; + } +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/Token.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/Token.java new file mode 100644 index 0000000..c115cbe --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/Token.java @@ -0,0 +1,14 @@ +package org.likelion.likelionkakaologin; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +@AllArgsConstructor +public class Token { + @JsonProperty("access_token") // JSON으로 직렬화 or 역직렬화할때 사용할 필드 이름을 지정 + private String accessToken; +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/TokenProvider.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/TokenProvider.java new file mode 100644 index 0000000..d15dbf9 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/TokenProvider.java @@ -0,0 +1,113 @@ +package org.likelion.likelionkakaologin; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.likelion.likelionkakaologin.user.User; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.security.Key; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.stream.Collectors; + +// jwt를 사용하여 인증 토큰을 생성, 파싱, 검증하는 클래스 +@Slf4j +@Component +public class TokenProvider { + private static final String AUTHORITIES_KEY = "auth"; + private final Key key; // jwt 서명을 위한 비밀 키. 토큰을 생성하고 검증할 때 사용 + private final long accessTokenValidityTime; // 액세스 토큰의 유효 시간 정의 + + @Autowired + public TokenProvider(@Value("${jwt.secret}") String secretKey, + @Value("${jwt.access-token-validity-in-milliseconds}") long accessTokenValidityTime) { + byte[] keyBytes = Decoders.BASE64.decode(secretKey); // secretKey를 Base64 디코딩 + this.key = Keys.hmacShaKeyFor(keyBytes); + this.accessTokenValidityTime = accessTokenValidityTime; + } + + // 정보와 시크릿 키, 시간을 넣어 압축해 토큰 생성 + public Token createToken(User user) { + long nowTime = (new Date()).getTime(); + + Date tokenExpiredTime = new Date(nowTime + accessTokenValidityTime); // 만료 시간 계산 + + String accessToken = Jwts.builder() + .setSubject(user.getId().toString()) // 토큰의 주체로 사용자의 id를 설정 + .claim(AUTHORITIES_KEY, user.getRole().name()) // 사용자 권한 정보 저장 + .setExpiration(tokenExpiredTime) // 토큰의 만료 시간 설정 + .signWith(key, SignatureAlgorithm.HS256) // 생성된 토큰에 서명 + .compact(); // 토큰 생성 + + return Token.builder() + .accessToken(accessToken) + .build(); + } + + // 토큰에서 인증 정보 추출 + public Authentication getAuthentication(String accessToken) { + Claims claims = parseClaims(accessToken); // 토큰을 파싱하여 클레임 추출 + + if (claims.get(AUTHORITIES_KEY) == null) { + throw new RuntimeException("권한 정보가 없는 토큰입니다."); + } + + // AUTHORITIES_KEY 라는 키를 사용해 권한 정보를 가져옴. + Collection authorities = + Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) // 권한 정보를 "ROLE_USER"와 "ROLE_ADMIN"로 분리 + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + + return new UsernamePasswordAuthenticationToken(claims.getSubject(), "", authorities); + // 위 객체는 사용자가 인증되었음을 나타내며, 이 객체를 인증 정보로 사용함. + // 이 객체를 통해 사용자의 권한을 확인하고, 사용자가 요청할 수 있는 리소스에 접근할 수 있도록 함. + } + + // HTTP 요청에서 토큰 추출(Bearer 라는 접두사 제거하고 실제 토큰 반환) + public String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); // HTTP 헤더에서 토큰 추출 + + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); // "Bearer " 문자열을 제거하고 토큰 반환 + } + + // Bearer 토큰이란: Authorization 헤더에 포함되어 사용자의 인증 상태를 서버에 전달하기 위해 사용됨. + // 즉, 토큰이 인증 수단으로 사용됨을 나타냄. + + return null; + } + + // 토큰 검증(토큰의 유효기간이 지났는지, 구조가 올바른지 등 체크) + public boolean validateToken(String token) { + try { + Jwts.parserBuilder() + .setSigningKey(key) // 서명 검증을 위해 키 설정 + .build() + .parseClaimsJws(token); // 토큰을 파싱하여 서명과 유효성 검증 + + return true; + } catch (UnsupportedJwtException | ExpiredJwtException | IllegalArgumentException e) { + return false; + } + } + + // 클레임 파싱 + private Claims parseClaims(String accessToken) { + try { + return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(accessToken).getBody(); + } catch (ExpiredJwtException e) { + return e.getClaims(); + } + } +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/user/Role.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/user/Role.java new file mode 100644 index 0000000..27eb2e9 --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/user/Role.java @@ -0,0 +1,5 @@ +package org.likelion.likelionkakaologin.user; + +public enum Role { + ROLE_USER, ROLE_ADMIN +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/user/User.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/user/User.java new file mode 100644 index 0000000..c65bdaf --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/user/User.java @@ -0,0 +1,32 @@ +package org.likelion.likelionkakaologin.user; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "users") +public class User { + + @Id + private Long id; + + @Column(name = "USER_NAME", nullable = false) + private String name; + + @Column(name = "USER_EMAIL") + private String email; + + @Column(name = "USER_PICTURE_URL") + private String pictureUrl; + + @Enumerated(EnumType.STRING) + @Column(name = "USER_ROLE", nullable = false) + private Role role; +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/user/UserRepository.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/user/UserRepository.java new file mode 100644 index 0000000..7ea887c --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/java/org/likelion/likelionkakaologin/user/UserRepository.java @@ -0,0 +1,9 @@ +package org.likelion.likelionkakaologin.user; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface UserRepository extends JpaRepository { + Optional findById(Long id); +} diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/resources/application.yml b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/resources/application.yml new file mode 100644 index 0000000..f17da9a --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/main/resources/application.yml @@ -0,0 +1,31 @@ +spring: + profiles: + active: prod + + datasource: + url: jdbc:h2:tcp://localhost/~/test + username: sa + password: + driver-class-name: org.h2.Driver + + jpa: + hibernate: + ddl-auto: create + properties: + hibernate: + show_sql: true + format_sql: true + open-in-view: false + +logging: + level: + org.hibernate.spl: debug + org.hibernate.type: trace + +kakao: + client_id: ${kakao.client_id} + redirect_uri: ${kakao.redirect_uri} + +jwt: + secret: ${jwt.secret} + access-token-validity-in-milliseconds: ${jwt.access-token-validity-in-milliseconds} \ No newline at end of file diff --git a/choseoyun/likelion-kakao-login/likelion-kakao-login/src/test/java/org/likelion/likelionkakaologin/LikelionKakaoLoginControllerApplicationTests.java b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/test/java/org/likelion/likelionkakaologin/LikelionKakaoLoginControllerApplicationTests.java new file mode 100644 index 0000000..d30ffec --- /dev/null +++ b/choseoyun/likelion-kakao-login/likelion-kakao-login/src/test/java/org/likelion/likelionkakaologin/LikelionKakaoLoginControllerApplicationTests.java @@ -0,0 +1,13 @@ +package org.likelion.likelionkakaologin; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class LikelionKakaoLoginControllerApplicationTests { + + @Test + void contextLoads() { + } + +}