feat(auth): implement JWT authentication interceptor
- Add JwtAuthInterceptor to validate JWT tokens on protected endpoints - Register interceptor paths via WebConfig for /api/** routes - Fix Result return type to support nullable values across auth flows
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
package com.msksbr.bookmgr.config
|
||||||
|
|
||||||
|
import com.msksbr.bookmgr.interceptor.JwtAuthInterceptor
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class WebConfig(
|
||||||
|
private val jwtAuthInterceptor: JwtAuthInterceptor
|
||||||
|
): WebMvcConfigurer {
|
||||||
|
override fun addInterceptors(registry: InterceptorRegistry) {
|
||||||
|
registry.addInterceptor(jwtAuthInterceptor)
|
||||||
|
.addPathPatterns("/api/**")
|
||||||
|
.excludePathPatterns("/api/auth/login")
|
||||||
|
.excludePathPatterns("/api/auth/logout")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ class AuthController(
|
|||||||
@RequestBody
|
@RequestBody
|
||||||
loginDTO: UserLoginDTO,
|
loginDTO: UserLoginDTO,
|
||||||
request: HttpServletRequest
|
request: HttpServletRequest
|
||||||
): Result<out Any> {
|
): Result<out Any?> {
|
||||||
log.info("Login from ${ipExtractor.getRealIp(request)} username: ${loginDTO.username}")
|
log.info("Login from ${ipExtractor.getRealIp(request)} username: ${loginDTO.username}")
|
||||||
log.info("UA: ${request.getHeader("User-Agent")}")
|
log.info("UA: ${request.getHeader("User-Agent")}")
|
||||||
// 调用service验证
|
// 调用service验证
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.msksbr.bookmgr.interceptor
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.msksbr.bookmgr.config.JwtUtils
|
||||||
|
import com.msksbr.bookmgr.template.Result
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class JwtAuthInterceptor(
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val jwtUtils: JwtUtils
|
||||||
|
) : HandlerInterceptor {
|
||||||
|
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
|
||||||
|
val authHeader = request.getHeader("Authorization") ?: run {
|
||||||
|
writeJson(
|
||||||
|
response, HttpServletResponse.SC_UNAUTHORIZED,
|
||||||
|
Result.unauthorized("Missing Authorization header")
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!authHeader.startsWith("Bearer ")) {
|
||||||
|
writeJson(
|
||||||
|
response, HttpServletResponse.SC_FORBIDDEN,
|
||||||
|
Result.unauthorized("Invalid token format")
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val token = authHeader.removePrefix("Bearer ")
|
||||||
|
val claims = jwtUtils.parseToken(token)
|
||||||
|
if (claims == null) {
|
||||||
|
writeJson(
|
||||||
|
response, HttpServletResponse.SC_UNAUTHORIZED,
|
||||||
|
Result.unauthorized("Token invalid or expired")
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
request.setAttribute("username",claims.subject)
|
||||||
|
request.setAttribute("role",claims.get("role", String::class.java))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeJson(response: HttpServletResponse, status: Int, result: Result<*>) {
|
||||||
|
response.status = status
|
||||||
|
response.contentType = "application/json;charset=UTF-8"
|
||||||
|
objectMapper.writeValue(response.writer, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ data class Result<T>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// 失败
|
// 失败
|
||||||
fun <T> error(message: String): Result<T> {
|
fun error(message: String): Result<Any?> {
|
||||||
return Result(
|
return Result(
|
||||||
code = 500,
|
code = 500,
|
||||||
message = message,
|
message = message,
|
||||||
@@ -23,7 +23,7 @@ data class Result<T>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// 未登录
|
// 未登录
|
||||||
fun <T> unauthorized(message: String): Result<T> {
|
fun unauthorized(message: String): Result<Any?> {
|
||||||
return Result(
|
return Result(
|
||||||
code = 401,
|
code = 401,
|
||||||
message = message,
|
message = message,
|
||||||
|
|||||||
Reference in New Issue
Block a user