-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from ClothingStoreService/feature/spring-securi…
…ty-apply Feature/spring security apply
- Loading branch information
Showing
99 changed files
with
4,051 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
src/main/kotlin/org/store/clothstar/common/config/CustomAuthenticationEntryPoint.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package org.store.clothstar.common.config | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import io.github.oshai.kotlinlogging.KotlinLogging | ||
import jakarta.servlet.ServletException | ||
import jakarta.servlet.http.HttpServletRequest | ||
import jakarta.servlet.http.HttpServletResponse | ||
import org.springframework.security.core.AuthenticationException | ||
import org.springframework.security.web.AuthenticationEntryPoint | ||
import org.springframework.stereotype.Component | ||
import org.store.clothstar.common.dto.MessageDTO | ||
import java.io.IOException | ||
|
||
@Component | ||
class CustomAuthenticationEntryPoint : AuthenticationEntryPoint { | ||
private val log = KotlinLogging.logger {} | ||
|
||
@Throws(IOException::class, ServletException::class) | ||
override fun commence( | ||
request: HttpServletRequest, | ||
response: HttpServletResponse, | ||
authException: AuthenticationException, | ||
) { | ||
log.error { "인증 실패 로직 실행" } | ||
response.status = HttpServletResponse.SC_UNAUTHORIZED | ||
response.characterEncoding = "UTF-8" | ||
response.contentType = "application/json" | ||
|
||
val messageDTO = MessageDTO( | ||
HttpServletResponse.SC_UNAUTHORIZED, | ||
"권한이 없습니다." | ||
) | ||
|
||
response.writer.write(ObjectMapper().writeValueAsString(messageDTO)) | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/main/kotlin/org/store/clothstar/common/config/CustomUserDetailsService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package org.store.clothstar.common.config | ||
|
||
import io.github.oshai.kotlinlogging.KotlinLogging | ||
import org.springframework.security.core.userdetails.UserDetails | ||
import org.springframework.security.core.userdetails.UserDetailsService | ||
import org.springframework.security.core.userdetails.UsernameNotFoundException | ||
import org.springframework.stereotype.Service | ||
import org.store.clothstar.common.error.ErrorCode | ||
import org.store.clothstar.member.domain.Account | ||
import org.store.clothstar.member.domain.CustomUserDetails | ||
import org.store.clothstar.member.repository.AccountRepository | ||
|
||
@Service | ||
class CustomUserDetailsService( | ||
private val accountRepository: AccountRepository, | ||
) : UserDetailsService { | ||
private val log = KotlinLogging.logger {} | ||
|
||
@Throws(UsernameNotFoundException::class) | ||
override fun loadUserByUsername(email: String): UserDetails { | ||
log.info { "loadUserByUsername() 실행" } | ||
val account: Account = accountRepository.findByEmail(email) | ||
?: throw UsernameNotFoundException(ErrorCode.NOT_FOUND_ACCOUNT.message) | ||
|
||
return CustomUserDetails(account) | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
...rg/store/clothstar/common/JasyptConfig.kt → ...e/clothstar/common/config/JasyptConfig.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
src/main/kotlin/org/store/clothstar/common/config/LoginFilter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package org.store.clothstar.common.config | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import io.github.oshai.kotlinlogging.KotlinLogging | ||
import jakarta.servlet.FilterChain | ||
import jakarta.servlet.ServletException | ||
import jakarta.servlet.http.HttpServletRequest | ||
import jakarta.servlet.http.HttpServletResponse | ||
import org.springframework.http.HttpStatus | ||
import org.springframework.security.authentication.AuthenticationManager | ||
import org.springframework.security.authentication.BadCredentialsException | ||
import org.springframework.security.authentication.DisabledException | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken | ||
import org.springframework.security.core.Authentication | ||
import org.springframework.security.core.AuthenticationException | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter | ||
import org.store.clothstar.common.config.jwt.JwtUtil | ||
import org.store.clothstar.common.dto.MessageDTO | ||
import org.store.clothstar.member.domain.CustomUserDetails | ||
import org.store.clothstar.member.dto.request.MemberLoginRequest | ||
import java.io.IOException | ||
|
||
class LoginFilter( | ||
private val authenticationManager: AuthenticationManager, | ||
private val jwtUtil: JwtUtil, | ||
) : UsernamePasswordAuthenticationFilter() { | ||
init { | ||
setFilterProcessesUrl("/v1/members/login") | ||
} | ||
|
||
private val log = KotlinLogging.logger {} | ||
|
||
/** | ||
* 로그인 창에서 입력한 id, password를 받아서 | ||
* Authentication Manager에 던져 줘야 하는데 그 DTO역할을 하는 객체가 UsernamePasswordAuthenticationToken이다. | ||
* Authentication Manager에 전달하면 최종적으로 Authentication에 전달 된다. | ||
* return 하면 Authentication Manager에 던져진다. | ||
* | ||
* | ||
* AuthenticationManager에 던지기 위해서 주입을 받아야 한다. | ||
*/ | ||
@Throws(AuthenticationException::class) | ||
override fun attemptAuthentication(request: HttpServletRequest, response: HttpServletResponse): Authentication { | ||
log.info { "로그인 진행" } | ||
|
||
try { | ||
val memberLoginRequest = ObjectMapper().readValue(request.inputStream, MemberLoginRequest::class.java) | ||
log.info { "login parameter memberLoginRequest: ${memberLoginRequest.toString()}" } | ||
|
||
val email = memberLoginRequest.email | ||
val password = memberLoginRequest.password | ||
|
||
val authTokenDTO = UsernamePasswordAuthenticationToken(email, password, null) | ||
return authenticationManager.authenticate(authTokenDTO) | ||
} catch (e: IOException) { | ||
throw RuntimeException(e) | ||
} | ||
} | ||
|
||
@Throws(IOException::class, ServletException::class) | ||
override fun successfulAuthentication( | ||
request: HttpServletRequest, | ||
response: HttpServletResponse, | ||
chain: FilterChain, | ||
authentication: Authentication, | ||
) { | ||
log.info { "로그인 성공" } | ||
val customUserDetails = authentication.principal as CustomUserDetails | ||
|
||
val account = customUserDetails.account | ||
log.info { "account: ${account.toString()}" } | ||
|
||
val accessToken = jwtUtil.createAccessToken(account) | ||
log.info { "생성 accessToken: Bearer $accessToken " } | ||
|
||
val refreshToken = jwtUtil.createRefreshToken(account) | ||
log.info { "생성 refreshToken: Bearer $refreshToken" } | ||
|
||
response.addHeader("Authorization", "Bearer $accessToken") | ||
response.addCookie(jwtUtil.createCookie("refreshToken", refreshToken)) | ||
response.status = HttpStatus.OK.value() | ||
response.characterEncoding = "UTF-8" | ||
response.contentType = "application/json" | ||
|
||
val messageDTO: MessageDTO = MessageDTO( | ||
HttpServletResponse.SC_OK, | ||
"로그인 성공 하였습니다." | ||
) | ||
|
||
response.writer.print(ObjectMapper().writeValueAsString(messageDTO)) | ||
} | ||
|
||
@Throws(IOException::class, ServletException::class) | ||
override fun unsuccessfulAuthentication( | ||
request: HttpServletRequest, | ||
response: HttpServletResponse, | ||
failed: AuthenticationException, | ||
) { | ||
log.info { "로그인 실패" } | ||
|
||
response.status = HttpServletResponse.SC_UNAUTHORIZED | ||
response.characterEncoding = "UTF-8" | ||
response.contentType = "application/json" | ||
|
||
val messageDTO: MessageDTO = MessageDTO( | ||
HttpServletResponse.SC_UNAUTHORIZED, | ||
errorMessage(failed), | ||
) | ||
|
||
response.writer.print(ObjectMapper().writeValueAsString(messageDTO)) | ||
} | ||
|
||
private fun errorMessage(failed: AuthenticationException): String? { | ||
return if (failed is BadCredentialsException) { | ||
"이메일 또는 비밀번호가 올바르지 않습니다. 다시 확인해주세요." | ||
} else if (failed is DisabledException) { | ||
"계정이 비활성화 되어있습니다. 이메일 인증을 완료해주세요" | ||
} else { | ||
null | ||
} | ||
} | ||
} |
95 changes: 95 additions & 0 deletions
95
src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package org.store.clothstar.common.config | ||
|
||
import org.springframework.boot.autoconfigure.security.servlet.PathRequest | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.http.HttpMethod | ||
import org.springframework.security.authentication.AuthenticationManager | ||
import org.springframework.security.config.Customizer | ||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity | ||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer | ||
import org.springframework.security.config.annotation.web.configurers.* | ||
import org.springframework.security.config.http.SessionCreationPolicy | ||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder | ||
import org.springframework.security.crypto.password.PasswordEncoder | ||
import org.springframework.security.web.SecurityFilterChain | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter | ||
import org.store.clothstar.common.config.jwt.JwtAuthenticationFilter | ||
import org.store.clothstar.common.config.jwt.JwtUtil | ||
|
||
@EnableWebSecurity | ||
@Configuration | ||
class SecurityConfiguration( | ||
private val authenticationConfiguration: AuthenticationConfiguration, | ||
private val jwtAuthenticationFilter: JwtAuthenticationFilter, | ||
private val jwtUtil: JwtUtil, | ||
) { | ||
@Bean | ||
fun passwordEncoder(): PasswordEncoder { | ||
return BCryptPasswordEncoder() | ||
} | ||
|
||
@Bean | ||
@Throws(Exception::class) | ||
fun authenticationManager(): AuthenticationManager { | ||
return authenticationConfiguration.authenticationManager | ||
} | ||
|
||
@Bean | ||
fun configure(): WebSecurityCustomizer { | ||
return WebSecurityCustomizer { web -> | ||
web.ignoring() | ||
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()) | ||
} | ||
} | ||
|
||
@Bean | ||
@Throws(java.lang.Exception::class) | ||
fun filterChain(http: HttpSecurity): SecurityFilterChain { | ||
http.cors { obj: CorsConfigurer<HttpSecurity> -> obj.disable() } | ||
.csrf { obj: CsrfConfigurer<HttpSecurity> -> obj.disable() } | ||
.httpBasic { obj: HttpBasicConfigurer<HttpSecurity> -> obj.disable() } | ||
.formLogin { obj: FormLoginConfigurer<HttpSecurity> -> obj.disable() } | ||
|
||
http.authorizeHttpRequests( | ||
Customizer { auth -> | ||
auth | ||
.requestMatchers( | ||
"/", "/login", "/userPage", "/sellerPage", "/adminPage", "/main", | ||
"/v1/members/login", "/signup", "/v1/members/email/**", "/v1/access", | ||
"/v1/categories/**", "/v1/products/**", "/v1/productLines/**", "/v2/productLines/**", | ||
"/productLinePagingSlice", "/productLinePagingOffset", | ||
"/v1/orderdetails", "/v1/orders", "membersPagingOffset", "membersPagingSlice", | ||
"/v1/orderdetails", "/v1/orders", "/v2/orders", "/v3/orders", "/v1/orders/list", | ||
"/v1/orders/list", "/ordersPagingOffset", "/ordersPagingSlice", "/v2/orders/list", | ||
"/v1/seller/orders/**", "/v1/seller/orders", "/v1/orders/**", "/v1/orderdetails/**", | ||
"/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**" | ||
).permitAll() | ||
.requestMatchers(HttpMethod.POST, "/v1/members").permitAll() | ||
.requestMatchers(HttpMethod.POST, "/v1/sellers/**").authenticated() | ||
.requestMatchers("/seller/**", "/v1/sellers/**").hasRole("SELLER") | ||
.requestMatchers("/admin/**", "/v1/admin/**").hasRole("ADMIN") | ||
.requestMatchers("v2/members", "v3/members").hasRole("ADMIN") | ||
.requestMatchers(HttpMethod.GET, "/v1/members").hasRole("ADMIN") | ||
.anyRequest().authenticated() | ||
} | ||
) | ||
|
||
//JWT 토큰 인증 방식 사용하기에 session 유지 비활성화 | ||
http.sessionManagement { sessionManagement -> | ||
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS) | ||
} | ||
|
||
//UsernamePasswordAuthenticationFilter 대신에 LoginFilter가 실행된다. | ||
//LoginFilter 이전에 jwtAhthenticationFilter가 실행된다. | ||
http.addFilterBefore(jwtAuthenticationFilter, LoginFilter::class.java) | ||
http.addFilterAt( | ||
LoginFilter(authenticationManager(), jwtUtil), | ||
UsernamePasswordAuthenticationFilter::class.java | ||
) | ||
|
||
return http.build() | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
src/main/kotlin/org/store/clothstar/common/config/SwaggerConfig.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package org.store.clothstar.common.config | ||
|
||
import io.swagger.v3.oas.models.Components | ||
import io.swagger.v3.oas.models.OpenAPI | ||
import io.swagger.v3.oas.models.info.Info | ||
import io.swagger.v3.oas.models.security.SecurityRequirement | ||
import io.swagger.v3.oas.models.security.SecurityScheme | ||
import org.springdoc.core.models.GroupedOpenApi | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
|
||
@Configuration | ||
class SwaggerConfig { | ||
private val BEARER_TOKEN = "Bearer Token" | ||
private val BEARER = "Bearer" | ||
private val AUTHORIZATION = "Authorization" | ||
private val JWT = "JWT" | ||
|
||
@Bean | ||
fun groupedAllOpenApi(): GroupedOpenApi { | ||
return GroupedOpenApi.builder() | ||
.group("All") | ||
.pathsToMatch("/v1/**", "/v2/**", "/v3/**") | ||
.build() | ||
} | ||
|
||
@Bean | ||
fun groupedMemberOpenApi(): GroupedOpenApi { | ||
return GroupedOpenApi.builder() | ||
.group("Member") | ||
.pathsToMatch("/v1/members/**", "/v1/sellers/**") | ||
.build() | ||
} | ||
|
||
@Bean | ||
fun springShopOpenAPI(): OpenAPI { | ||
val info = Info().title("clothstar-v3 Project API") | ||
.description("의류 쇼핑몰 3차 프로젝트 입니다.") | ||
.version("v0.3") | ||
|
||
val apiKey = SecurityScheme() | ||
.type(SecurityScheme.Type.HTTP) | ||
.`in`(SecurityScheme.In.HEADER) | ||
.name(AUTHORIZATION) | ||
.scheme(BEARER) | ||
.bearerFormat(JWT) | ||
|
||
val securityRequirement = SecurityRequirement() | ||
.addList(BEARER_TOKEN) | ||
|
||
return OpenAPI().info(info) | ||
.components(Components().addSecuritySchemes(BEARER_TOKEN, apiKey)) | ||
.addSecurityItem(securityRequirement) | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
src/main/kotlin/org/store/clothstar/common/config/exception/BaseException.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.store.clothstar.common.config.exception | ||
|
||
class BaseException( | ||
private val exceptionType: ExceptionType | ||
) : RuntimeException(exceptionType.message()) |
Oops, something went wrong.