Move user infos to JWT tokens #19
|
@ -32,7 +32,7 @@ dependencies {
|
|||
implementation("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.3")
|
||||
implementation("javax.xml.bind:jaxb-api:2.3.0")
|
||||
implementation("io.jsonwebtoken:jjwt:0.9.1")
|
||||
implementation("io.jsonwebtoken:jjwt-api:0.11.2")
|
||||
implementation("org.apache.poi:poi-ooxml:4.1.0")
|
||||
implementation("org.apache.pdfbox:pdfbox:2.0.4")
|
||||
implementation("dev.fyloz.colorrecipesexplorer:database-manager:5.2")
|
||||
|
@ -59,7 +59,7 @@ dependencies {
|
|||
runtimeOnly("org.postgresql:postgresql:42.2.16")
|
||||
runtimeOnly("com.microsoft.sqlserver:mssql-jdbc:9.2.1.jre11")
|
||||
|
||||
implementation("org.springframework.cloud:spring-cloud-starter:2.2.8.RELEASE")
|
||||
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.2")
|
||||
}
|
||||
|
||||
springBoot {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package dev.fyloz.colorrecipesexplorer
|
||||
|
||||
public typealias SpringUser = org.springframework.security.core.userdetails.User
|
|
@ -1,23 +1,22 @@
|
|||
package dev.fyloz.colorrecipesexplorer.config.security
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import dev.fyloz.colorrecipesexplorer.SpringUser
|
||||
import dev.fyloz.colorrecipesexplorer.config.properties.CreSecurityProperties
|
||||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
import dev.fyloz.colorrecipesexplorer.model.account.UserLoginRequest
|
||||
import dev.fyloz.colorrecipesexplorer.utils.buildJwt
|
||||
import io.jsonwebtoken.ExpiredJwtException
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.core.userdetails.User
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
||||
import org.springframework.util.Assert
|
||||
import org.springframework.web.util.WebUtils
|
||||
import java.util.*
|
||||
import javax.servlet.FilterChain
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
@ -28,7 +27,7 @@ val blacklistedJwtTokens = mutableListOf<String>()
|
|||
|
||||
class JwtAuthenticationFilter(
|
||||
private val authManager: AuthenticationManager,
|
||||
private val securityConfigurationProperties: CreSecurityProperties,
|
||||
private val securityProperties: CreSecurityProperties,
|
||||
private val updateUserLoginTime: (Long) -> Unit
|
||||
) : UsernamePasswordAuthenticationFilter() {
|
||||
private var debugMode = false
|
||||
|
@ -49,29 +48,17 @@ class JwtAuthenticationFilter(
|
|||
chain: FilterChain,
|
||||
authResult: Authentication
|
||||
) {
|
||||
val jwtSecret = securityConfigurationProperties.jwtSecret
|
||||
val jwtDuration = securityConfigurationProperties.jwtDuration
|
||||
Assert.notNull(jwtSecret, "No JWT secret has been defined.")
|
||||
Assert.notNull(jwtDuration, "No JWT duration has been defined.")
|
||||
val userId = (authResult.principal as User).username
|
||||
updateUserLoginTime(userId.toLong())
|
||||
val expirationMs = System.currentTimeMillis() + jwtDuration
|
||||
val expirationDate = Date(expirationMs)
|
||||
val token = Jwts.builder()
|
||||
.setSubject(userId)
|
||||
.setExpiration(expirationDate)
|
||||
.signWith(SignatureAlgorithm.HS512, jwtSecret.toByteArray())
|
||||
.compact()
|
||||
response.addHeader("Access-Control-Expose-Headers", "X-Authentication-Expiration")
|
||||
val user = (authResult.principal as SpringUser)
|
||||
val token = user.buildJwt(securityProperties)
|
||||
|
||||
var bearerCookie =
|
||||
"$authorizationCookieName=Bearer$token; Max-Age=${jwtDuration / 1000}; HttpOnly; SameSite=strict"
|
||||
"$authorizationCookieName=Bearer$token; Max-Age=${securityProperties.jwtDuration / 1000}; HttpOnly; SameSite=strict"
|
||||
if (!debugMode) bearerCookie += "; Secure;"
|
||||
response.addHeader(
|
||||
"Set-Cookie",
|
||||
bearerCookie
|
||||
)
|
||||
response.addHeader(authorizationCookieName, "Bearer $token")
|
||||
response.addHeader("X-Authentication-Expiration", "$expirationMs")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +102,7 @@ class JwtAuthorizationFilter(
|
|||
val jwtSecret = securityConfigurationProperties.jwtSecret
|
||||
Assert.notNull(jwtSecret, "No JWT secret has been defined.")
|
||||
return try {
|
||||
|
||||
val userId = Jwts.parser()
|
||||
.setSigningKey(jwtSecret.toByteArray())
|
||||
.parseClaimsJws(token.replace("Bearer", ""))
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package dev.fyloz.colorrecipesexplorer.utils
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.SpringUser
|
||||
import dev.fyloz.colorrecipesexplorer.config.properties.CreSecurityProperties
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import java.util.*
|
||||
|
||||
data class Jwt(
|
||||
val subject: String,
|
||||
val secret: String,
|
||||
val duration: Long,
|
||||
val signatureAlgorithm: SignatureAlgorithm = SignatureAlgorithm.HS512
|
||||
)
|
||||
|
||||
fun SpringUser.buildJwt(properties: CreSecurityProperties) =
|
||||
Jwt(this.username, properties.jwtSecret, properties.jwtDuration).build()
|
||||
|
||||
fun Jwt.build(): String {
|
||||
val expirationMs = System.currentTimeMillis() + this.duration
|
||||
val expirationDate = Date(expirationMs)
|
||||
|
||||
val base64Secret = Encoders.BASE64.encode(this.secret.toByteArray())
|
||||
val key = Keys.hmacShaKeyFor(base64Secret.toByteArray())
|
||||
|
||||
return Jwts.builder()
|
||||
.setSubject(this.subject)
|
||||
.setExpiration(expirationDate)
|
||||
.signWith(key)
|
||||
.compact()
|
||||
}
|
Loading…
Reference in New Issue