Авторизация и аутентификация в Kotlin: полное руководство
В современной веб-разработке на Kotlin безопасность приложения начинается с правильной реализации аутентификации (проверки подлинности пользователя) и авторизации (проверки прав доступа). В этой статье мы рассмотрим основные подходы, библиотеки и инструменты для реализации этих механизмов на платформах Spring Boot и Ktor.
Основные понятия
Прежде чем перейти к коду, разберемся с терминами:
- Аутентификация — процесс проверки, что пользователь является тем, за кого себя выдает (обычно через логин/пароль, токен или биометрию).
- Авторизация — определение прав доступа аутентифицированного пользователя (например, доступ к определенным маршрутам или данным).
- JWT (JSON Web Token) — компактный и самодостаточный токен для передачи данных между сторонами.
- BCrypt — алгоритм хеширования паролей с солью, устойчивый к атакам перебором.
Аутентификация с помощью JWT в Spring Boot
Spring Security предоставляет мощную инфраструктуру для аутентификации. Рассмотрим реализацию JWT аутентификации с использованием библиотеки io.jsonwebtoken:jjwt.
// build.gradle.kts
dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("io.jsonwebtoken:jjwt-api:0.12.3")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.3")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.3")
}
Создадим утилитный класс для работы с JWT:
import io.jsonwebtoken.*
import io.jsonwebtoken.security.Keys
import org.springframework.stereotype.Component
import java.security.Key
import java.util.*
@Component
class JwtUtil {
private val secretKey: Key = Keys.secretKeyFor(SignatureAlgorithm.HS256)
private val expirationMs: Long = 3600000 // 1 час
fun generateToken(username: String): String {
return Jwts.builder()
.subject(username)
.issuedAt(Date())
.expiration(Date(System.currentTimeMillis() + expirationMs))
.signWith(secretKey)
.compact()
}
fun extractUsername(token: String): String? {
return try {
Jwts.parser().verifyWith(secretKey).build()
.parseSignedClaims(token).payload.subject
} catch (e: Exception) {
null
}
}
fun validateToken(token: String): Boolean {
return try {
Jwts.parser().verifyWith(secretKey).build()
.parseSignedClaims(token)
true
} catch (e: JwtException) {
false
}
}
}
Фильтр аутентификации для Spring Security
Теперь создадим фильтр, который будет перехватывать запросы и проверять JWT токен:
import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter
@Component
class JwtAuthFilter(
private val jwtUtil: JwtUtil,
private val userDetailsService: org.springframework.security.core.userdetails.UserDetailsService
) : OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
chain: FilterChain
) {
val authHeader = request.getHeader("Authorization")
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
chain.doFilter(request, response)
return
}
val token = authHeader.substring(7)
val username = jwtUtil.extractUsername(token)
if (username != null && SecurityContextHolder.getContext().authentication == null) {
val userDetails = userDetailsService.loadUserByUsername(username)
if (jwtUtil.validateToken(token)) {
val authToken = UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.authorities
)
authToken.details = WebAuthenticationDetailsSource().buildDetails(request)
SecurityContextHolder.getContext().authentication = authToken
}
}
chain.doFilter(request, response)
}
}
Аутентификация в Ktor
Фреймворк Ktor имеет встроенную поддержку аутентификации через плагин ktor-server-auth. Рассмотрим пример с JWT:
// build.gradle.kts
dependencies {
implementation("io.ktor:ktor-server-auth:2.3.7")
implementation("io.ktor:ktor-server-auth-jwt:2.3.7")
implementation("com.auth0:java-jwt:4.4.0")
}
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
fun Application.authModule() {
// Настройка JWT аутентификации
install(Authentication) {
jwt("auth