Merge pull request #42 from beaniejoy/feature/41

Cafe 리스트 검색 api 수정 및 전체적인 리팩토링
This commit is contained in:
Hanbin Lee
2023-04-12 00:56:07 +09:00
committed by GitHub
20 changed files with 100 additions and 135 deletions

View File

@@ -1,3 +1,5 @@
DROP PROCEDURE IF EXISTS insertCafeImages;
DELIMITER $$
CREATE PROCEDURE insertCafeImages()
BEGIN
@@ -15,10 +17,10 @@ BEGIN
WHILE(j <= 3) DO
INSERT IGNORE INTO `cafe_image` (img_url, created_at, created_by, updated_at, updated_by, cafe_id)
VALUES (CONCAT('test_img_url_', idx_img), now(), 'system', now(), 'system', var_cafe_id);
VALUES (CONCAT('https://d3qy02qh8hbgxp.cloudfront.net/cafe', idx_img, '.jpg'), now(), 'system', now(), 'system', var_cafe_id);
SET j = j + 1;
SET idx_img = idx_img + 1;
SET idx_img = idx_img % 7 + 1;
END WHILE;
SET i = i + 1;

View File

@@ -1,6 +1,6 @@
package io.beaniejoy.dongnecafe.common.config
import io.beaniejoy.dongnecafe.security.JwtTokenUtils
import io.beaniejoy.dongnecafe.security.utils.JwtTokenUtils
import io.beaniejoy.dongnecafe.security.config.JwtAuthenticationConfigurer
import io.beaniejoy.dongnecafe.security.handler.CustomAccessDeniedHandler
import io.beaniejoy.dongnecafe.security.handler.CustomAuthenticationEntryPoint

View File

@@ -1,7 +1,7 @@
package io.beaniejoy.dongnecafe.controller
import io.beaniejoy.dongnecafe.common.response.ApplicationResponse
import io.beaniejoy.dongnecafe.security.JwtTokenUtils
import io.beaniejoy.dongnecafe.security.utils.JwtTokenUtils
import io.beaniejoy.dongnecafe.domain.member.model.request.SignInRequest
import io.beaniejoy.dongnecafe.model.TokenResponse
import io.beaniejoy.dongnecafe.service.AuthService

View File

@@ -1,52 +0,0 @@
package io.beaniejoy.dongnecafe.security.filter
import io.beaniejoy.dongnecafe.security.JwtTokenUtils
import io.beaniejoy.dongnecafe.security.constant.SecurityConstant.BEARER
import io.beaniejoy.dongnecafe.security.constant.SecurityConstant.WHITESPACE
import mu.KotlinLogging
import org.springframework.http.HttpHeaders
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.filter.GenericFilterBean
import javax.servlet.FilterChain
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
class JwtAuthenticationFilter(
private val jwtTokenUtils: JwtTokenUtils
) : GenericFilterBean() {
private val log = KotlinLogging.logger {}
/**
* JWT access token 인증 처리
*/
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
val httpRequest = request as HttpServletRequest
log.info { "[JwtAuthenticationFilter][${request.dispatcherType}] uri: ${request.requestURI}" }
getAccessToken(httpRequest)?.let {
jwtTokenUtils.getAuthentication(it)
}?.also {
SecurityContextHolder.getContext().authentication = it
log.info { "Valid Access Token [${it.name}]" }
}
chain.doFilter(request, response)
}
private fun getAccessToken(request: HttpServletRequest): String? {
val bearer = request.getHeader(HttpHeaders.AUTHORIZATION)
?: return null
val splitBearer = bearer.split(WHITESPACE)
if (splitBearer.first() != BEARER) {
return null
}
if (splitBearer.size != 2 || splitBearer.last().isBlank()) {
return null
}
return splitBearer.last()
}
}

View File

@@ -1,3 +1,6 @@
server:
port: 9090
spring:
profiles:
active: local
@@ -5,22 +8,17 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: none # use flyway migration
ddl-auto: none
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
dialect: org.hibernate.dialect.MySQL8Dialect
format_sql: true
show-sql: false
open-in-view:
flyway:
enabled: false
devtools:
livereload:
enabled: false # no use devtools' LiveReload Server
server:
port: 9090
logging:
level:
org.hibernate.SQL: debug # logger 통해 로깅

View File

@@ -1,6 +1,6 @@
package io.beaniejoy.dongnecafe.common.config
import io.beaniejoy.dongnecafe.security.getAuthPrincipal
import io.beaniejoy.dongnecafe.security.utils.getAuthPrincipal
import mu.KLogging
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

View File

@@ -1,8 +1,12 @@
package io.beaniejoy.dongnecafe.domain.cafe.repository
import io.beaniejoy.dongnecafe.domain.cafe.entity.Cafe
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository
interface CafeRepository : JpaRepository<Cafe, Long> {
fun findByName(name: String): Cafe?
fun findByNameContainingIgnoreCase(name: String?, pageable: Pageable): Page<Cafe>
}

View File

@@ -1,7 +1,7 @@
package io.beaniejoy.dongnecafe.security.config
import io.beaniejoy.dongnecafe.security.JwtTokenUtils
import io.beaniejoy.dongnecafe.security.filter.JwtAuthenticationFilter
import io.beaniejoy.dongnecafe.security.utils.JwtTokenUtils
import org.springframework.security.config.annotation.SecurityConfigurerAdapter
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.DefaultSecurityFilterChain

View File

@@ -6,4 +6,6 @@ object SecurityConstant {
const val ANONYMOUS_USER = "anonymousUser"
const val ROLE_ANONYMOUS = "ROLE_ANONYMOUS"
const val JWT_AUTHORITIES_KEY = "authorities"
}

View File

@@ -1,6 +1,6 @@
package io.beaniejoy.dongnecafe.security.filter
import io.beaniejoy.dongnecafe.security.JwtTokenUtils
import io.beaniejoy.dongnecafe.security.utils.JwtTokenUtils
import io.beaniejoy.dongnecafe.security.constant.SecurityConstant.BEARER
import io.beaniejoy.dongnecafe.security.constant.SecurityConstant.WHITESPACE
import mu.KotlinLogging

View File

@@ -1,5 +1,7 @@
package io.beaniejoy.dongnecafe.security
package io.beaniejoy.dongnecafe.security.utils
import io.beaniejoy.dongnecafe.security.SecurityUser
import io.beaniejoy.dongnecafe.security.constant.SecurityConstant.JWT_AUTHORITIES_KEY
import io.jsonwebtoken.Claims
import io.jsonwebtoken.ExpiredJwtException
import io.jsonwebtoken.Jwts
@@ -24,9 +26,7 @@ class JwtTokenUtils(
private val key: Key = Keys.hmacShaKeyFor(secretKey.toByteArray())
private val validityTimeMilliSec: Long = validityTimeSec * 1000
companion object : KLogging() {
const val AUTHORITIES_KEY = "authorities"
}
companion object : KLogging()
fun createToken(authentication: Authentication): String {
val authenticatedMember = (authentication.principal as SecurityUser).member
@@ -37,7 +37,7 @@ class JwtTokenUtils(
return Jwts.builder()
.setSubject(authenticatedMember.email)
.claim(AUTHORITIES_KEY, authorities)
.claim(JWT_AUTHORITIES_KEY, authorities)
.signWith(key, SignatureAlgorithm.HS256)
.setExpiration(expirationDate)
.compact()
@@ -47,7 +47,7 @@ class JwtTokenUtils(
val claims = getValidTokenBody(accessToken)
?: return null
val authorities = claims[AUTHORITIES_KEY].toString().split(",")
val authorities = claims[JWT_AUTHORITIES_KEY].toString().split(",")
.map { SimpleGrantedAuthority(it) }
return UsernamePasswordAuthenticationToken(claims.subject, accessToken, authorities)
@@ -62,10 +62,10 @@ class JwtTokenUtils(
.parseClaimsJws(accessToken)
.body
} catch (e: ExpiredJwtException) {
logger.error { "JWT access token expired. > Error: ${e.message}" }
logger.info { "JWT access token expired. > Error: ${e.message}" }
null
} catch (e: Exception) {
logger.error { "JWT access token invalid. > Error: ${e.message}" }
logger.info { "JWT access token invalid. > Error: ${e.message}" }
null
}
}

View File

@@ -1,4 +1,4 @@
package io.beaniejoy.dongnecafe.security
package io.beaniejoy.dongnecafe.security.utils
import io.beaniejoy.dongnecafe.security.constant.SecurityConstant.ANONYMOUS_USER
import io.beaniejoy.dongnecafe.security.constant.SecurityConstant.ROLE_ANONYMOUS

View File

@@ -1,7 +1,9 @@
package io.beaniejoy.dongnecafe.common.config
import io.beaniejoy.dongnecafe.security.JwtAuthenticationConfigurer
import io.beaniejoy.dongnecafe.security.JwtTokenUtils
import io.beaniejoy.dongnecafe.security.config.JwtAuthenticationConfigurer
import io.beaniejoy.dongnecafe.security.utils.JwtTokenUtils
import io.beaniejoy.dongnecafe.security.handler.CustomAccessDeniedHandler
import io.beaniejoy.dongnecafe.security.handler.CustomAuthenticationEntryPoint
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.security.servlet.PathRequest
import org.springframework.context.annotation.Bean
@@ -18,6 +20,12 @@ class SecurityConfig {
@Autowired
lateinit var jwtTokenUtils: JwtTokenUtils
@Autowired
lateinit var customAccessDeniedHandler: CustomAccessDeniedHandler
@Autowired
lateinit var customAuthenticationEntryPoint: CustomAuthenticationEntryPoint
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
return http
@@ -26,7 +34,8 @@ class SecurityConfig {
.formLogin().disable()
.authorizeRequests()
.anyRequest().authenticated()
// .anyRequest().authenticated()
.anyRequest().permitAll()
.and()
.sessionManagement()
@@ -34,6 +43,11 @@ class SecurityConfig {
.and()
.also { jwtAuthenticationConfigurer(it) }
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint) // 인증 예외 entryPoint 적용
.accessDeniedHandler(customAccessDeniedHandler) // 인가 예외 handler 적용
.and()
.build()
}

View File

@@ -40,9 +40,10 @@ class CafeController(
*/
@GetMapping
fun searchCafeList(
@RequestParam("name") name: String?,
@PageableDefault(sort = ["name"], direction = Sort.Direction.ASC, page = 0, size = 10) pageable: Pageable
): ApplicationResponse<Page<CafeSearchInfo>> {
val searchCafes = cafeService.searchCafeList(pageable)
val searchCafes = cafeService.searchCafeList(name, pageable)
return ApplicationResponse
.success()

View File

@@ -59,8 +59,8 @@ class CafeService(
}
}
fun searchCafeList(pageable: Pageable): Page<CafeSearchInfo> {
val cafeList: Page<Cafe> = cafeRepository.findAll(pageable)
fun searchCafeList(name: String?, pageable: Pageable): Page<CafeSearchInfo> {
val cafeList: Page<Cafe> = cafeRepository.findByNameContainingIgnoreCase(name, pageable)
return cafeList.map { CafeSearchInfo.of(it) }
}

View File

@@ -1,25 +0,0 @@
package io.beaniejoy.dongnecafe.security
import io.beaniejoy.dongnecafe.security.filter.JwtAuthenticationFilter
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
class JwtAuthenticationConfigurer :
SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>() {
private lateinit var jwtTokenUtils: JwtTokenUtils
override fun configure(http: HttpSecurity) {
http
.addFilterBefore(
JwtAuthenticationFilter(this.jwtTokenUtils),
UsernamePasswordAuthenticationFilter::class.java
)
}
fun jwtTokenUtils(jwtTokenUtils: JwtTokenUtils): JwtAuthenticationConfigurer {
this.jwtTokenUtils = jwtTokenUtils
return this
}
}

View File

@@ -5,10 +5,10 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: none # flyway migration 사용
ddl-auto: none
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
dialect: org.hibernate.dialect.MySQL8Dialect
format_sql: true
show-sql: false
devtools:

View File

@@ -1,39 +1,60 @@
#!/bin/bash
echo -e "########### [LOCAL] DB Migration ###########"
printf "\n"
PROJECT_NAME="dongne-cafe-api"
PROJECT_ROOT_DIR=$(pwd)
FLYWAY_CONFIG_FILE="flyway-local.conf"
if [[ $PROJECT_ROOT_DIR != *"/$PROJECT_NAME" ]];
then
echo "Error >> move to project's root directory"
exit 1
fi
check_project_root_path() {
if [[ $PROJECT_ROOT_DIR != *"/$PROJECT_NAME" ]];
then
echo "Error >> move to project's root directory"
exit 1
fi
echo "Project's Root Directory: $PROJECT_ROOT_DIR"
printf "\n"
echo -e "Project's Root Directory: $PROJECT_ROOT_DIR\n"
}
echo "###################################"
echo "Using Flyway Version"
flyway_version_check() {
echo "###################################"
echo "Using Flyway Version"
if ! flyway --version 2> /dev/null;
then
echo "Error >> Flyway Not Supported"
exit 1
fi
echo "###################################"
printf "\n"
if ! flyway --version 2> /dev/null;
then
echo "Error >> Flyway Not Supported"
exit 1
fi
echo -e "###################################\n"
}
echo "1. Flyway Info"
flyway info -configFiles="$PROJECT_ROOT_DIR/db/$FLYWAY_CONFIG_FILE"
printf "\n"
error_check() {
if [ $? -ne 0 ];
then
echo "Error >> $1 & Exit"
exit 1
fi
echo "2. Flyway Migrate"
flyway migrate -configFiles="$PROJECT_ROOT_DIR/db/$FLYWAY_CONFIG_FILE"
printf "\n"
printf "\n"
}
echo "3. Flyway Validate"
flyway validate -configFiles="$PROJECT_ROOT_DIR/db/$FLYWAY_CONFIG_FILE"
flyway_migration_process() {
STEP_1="1. Flyway Info"
STEP_2="2. Flyway Migrate"
STEP_3="3. Flyway Validate"
echo $STEP_1
flyway info -configFiles="$PROJECT_ROOT_DIR/db/$FLYWAY_CONFIG_FILE"
error_check "$STEP_1"
echo $STEP_2
flyway migrate -configFiles="$PROJECT_ROOT_DIR/db/$FLYWAY_CONFIG_FILE" -outputType=json
error_check "$STEP_2"
echo $STEP_3
flyway validate -configFiles="$PROJECT_ROOT_DIR/db/$FLYWAY_CONFIG_FILE" -outputType=json
error_check "$STEP_3"
}
echo "########### [LOCAL] DB Migration ###########"
check_project_root_path
flyway_version_check
flyway_migration_process