์ด์ ์ Spring Security ์์ด ๊ตฌํํ ์ธ์ฆ์๋ ๊ถํ์ ๋ํ ๊ฒ์ฆ์ด ํฌํจ๋์ง ์์๋ค.
์ด๋ฒ์๋ ๊ถํ์ ๋ํ ๊ฒ์ฆ๊ณผ ๊ฐ์ URI์ด๋๋ผ๋ Http Method
์ ๋ฐ๋ผ ์ธ์ฆํ์์ฌ๋ถ๋ฅผ ๋ถ๊ธฐ์ํค๋ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ณ ์ ํ๋ค.
Jwt ํ ํฐ ์์ฑ์ Claim์ผ๋ก ๊ถํ์ ๊ฐ๋๋ก ํ๋ค.
๋ค์์ผ๋ก Jwt์ ๋ํ ์ฒ๋ฆฌ๋ก์ง์ ๋ค๋ฃจ๋ JwtUtils ํด๋์ค์ ๊ถํ๊ฒ์ฆ์ ์ํํ๋ ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋ค.
fun verifySellerToken(token: String): Boolean {
try {
val claims = getAllClaims(token)
val expiration = claims.expiration // ๋ง๋ฃ๊ธฐ๊ฐ ๊ฒ์ฆ
if (!expiration.after(Date())) return false
when(val role = claims["role"]) {
is String -> {
return role.equals("ROLE_SELLER") // ๊ถํ๊ฒ์ฆ
}
else -> throw IllegalArgumentException("ํ์
์๋ฌ")
}
} catch (e: JwtException) {
return false
} catch (e: IllegalArgumentException) {
return false
}
}
ํ๋งค์ ๊ถํ(ROLE_SELLER)์ ๊ฒ์ฆ์ด ํ์ํ ๊ฒฝ์ฐ ์๋ ์ธํฐ์ ํฐ๋ฅผ ํตํ๋๋ก ์ค์ ํด์ค๋ค.
@Component
class RoleVerifyInterceptor(private val jwtTokenProvider: JwtTokenProvider): HandlerInterceptor {
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
if(isPreRequest(request)) return true
val token = JwtTokenExtractor.extract(request)
token?.let {
if(jwtTokenProvider.verifySellerToken(it)) return true
}
throw AuthenticationException("์ธ์ฆ์คํจ")
}
private fun isPreRequest(request: HttpServletRequest): Boolean {
return request.method.equals(HttpMethod.OPTIONS)
}
}
๊ธฐ์กด์๋ ์์ฒญ URI๋ก๋ง ์ธ์ฆ์ ์ํํ๋ ์ธํฐ์
ํฐ์ ์ ์ฉ์ฌ๋ถ๋ฅผ ํ๋จํ๋ค.
ํ์ง๋ง Rest API์ ๊ฒฝ์ฐ ๊ฐ์ URI์ Http Method๋ง ๋ฌ๋ฆฌํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๋ค.
ex)
GET /articles => ๊ฒ์๊ธ ์กฐํ (์ธ์ฆ ๋ถํ์)
POST /articles => ๊ฒ์๊ธ ๋ฑ๋ก (์ธ์ฆ ํ์)
์ธํฐ์
ํฐ์์๋ HttpServletRequest
๋ฅผ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ ์์ฒญ URI์ HTTP ๋ฉ์๋๋ฅผ ์์๋ผ ์ ์๋ค. ํ์ง๋ง ๋ชจ๋ ์์ฒญ์ ๋ฉ์๋๋ง๋ค ๋ถ๊ธฐ์์
์ ํ๋ ๊ฒ์ ๋๋ฌด ๋ฒ๊ฑฐ๋กญ๊ณ ๋ฐ๋ณต์ ์ธ ์์
์ด ๋ ๊ฒ์ด๋ค.
(if... if ... if ... if..........)
์ด๋ฐ ๋ฐ๋ณต์ ์ธ ์์ ์ ์ค์ด๊ธฐ ์ํด ์ธ์ฆ ์ธํฐ์ ํฐ ์ ์ฉ์ฌ๋ถ๋ฅผ ํ๋จํ๊ธฐ ์ํ ๋ก์ง์ ๋ผ์ด๋ผ ๊ฒ์ด๋ค.
class PatternMatcherInterceptor(
var targetInterceptor: HandlerInterceptor, // ์ ์ฉ๋ ์ธํฐ์
ํฐ
) : HandlerInterceptor {
var addPathPatterns: MutableMap<String, HttpMethod> = mutableMapOf() // ์ธํฐ์
ํฐ๋ฅผ ์ ์ฉ์กฐ๊ฑด <์์ฒญURI, ์์ฒญ๋ฉ์๋>
fun addPathPatterns(uri: String, httpMethod: HttpMethod) = this.addPathPatterns.put(uri, httpMethod)
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
val method = request.method
val requestURI = request.requestURI
// URI์ ์์ฒญ๋ฉ์๋๊ฐ ์ผ์นํ๋ ๊ฒฝ์ฐ targetInterceptor ๋ฅผ ํตํ๋๋ก ์ฒ๋ฆฌ
for (uri in addPathPatterns.keys) {
if (requestURI.equals(uri) && addPathPatterns[uri].toString() == method) {
return targetInterceptor.preHandle(request,response, handler)
}
}
return true
}
}
addPathPatterns
)addPathPatterns
์ ์กด์ฌํ๋ค๋ฉด ์ค์ ๊ฒ์ฆ์ ์ํํ ์ธํฐ์
ํฐ๋ฅผ ํธ์ถํ๋ค. (targetInterceptor.preHandle
)์ค์ ์ธ์ฆ์ ์ํํ๋ ์ธํฐ์ ํฐ ์ ๋จ์์ ํ๋ก์ ๋๋์ผ๋ก ์์ฒญ์ ๋งค์นญํด์ฃผ๋ ์ธํฐ์ ํฐ๋ฅผ ์ถ๊ฐํ ๊ฒ์ด๋ค.
@Configuration
class WebMvcConfig(
private val tokenVerifyInterceptor: TokenVerifyInterceptor,
private val roleVerifyInterceptor: RoleVerifyInterceptor,
private val authenticatedUserArgumentResolver: AuthenticatedUserArgumentResolver) : WebMvcConfigurer{
override fun addInterceptors(registry: InterceptorRegistry) {
val patternMatcherInterceptor1 = PatternMatcherInterceptor(roleVerifyInterceptor) // ํ๋งค์ ๊ถํ ๊ฒ์ฆ์ ์ํ ์ธํฐ์
ํฐ
patternMatcherInterceptor1.addPathPatterns("/test/auth-test/seller", HttpMethod.GET)
val patternMatcherInterceptor2 = PatternMatcherInterceptor(tokenVerifyInterceptor) // ์ผ๋ฐ ๊ฒ์ฆ ์ธํฐ์
ํฐ
patternMatcherInterceptor2.addPathPatterns("/items", HttpMethod.POST)
patternMatcherInterceptor2.addPathPatterns("/test/auth-test", HttpMethod.GET)
registry.addInterceptor(patternMatcherInterceptor1)
.addPathPatterns("/**")
registry.addInterceptor(patternMatcherInterceptor2)
.addPathPatterns("/**")
}
override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
resolvers.add(authenticatedUserArgumentResolver)
}
}