db connected

This commit is contained in:
Andrey Kassaev 2024-01-01 11:06:45 +04:00
parent 134ae88bd3
commit 5f0a7fe2a3
21 changed files with 208 additions and 118 deletions

View File

@ -3,8 +3,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "3.2.0"
id("io.spring.dependency-management") version "1.1.4"
kotlin("jvm") version "1.9.20"
kotlin("plugin.spring") version "1.9.20"
kotlin("jvm") version "1.9.22"
kotlin("plugin.spring") version "1.9.22"
kotlin("plugin.jpa") version "1.9.22"
}
group = "com.kassaev"
@ -19,20 +20,23 @@ repositories {
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
implementation("org.springframework.boot:spring-boot-starter-web:3.2.1")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.16.1")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.22")
compileOnly("org.projectlombok:lombok:1.18.30")
annotationProcessor("org.projectlombok:lombok:1.18.30")
implementation ("org.springframework.boot:spring-boot-starter-security")
testImplementation("org.springframework.security:spring-security-test")
implementation("org.springframework.boot:spring-boot-starter-security:3.2.1")
implementation("io.jsonwebtoken:jjwt-api:0.12.3")
implementation("io.jsonwebtoken:jjwt-impl:0.12.3")
implementation("io.jsonwebtoken:jjwt-jackson:0.12.3")
implementation("org.postgresql:postgresql:42.7.1")
implementation("org.springframework.boot:spring-boot-starter-data-jpa:3.2.1")
}
tasks.withType<KotlinCompile> {

View File

@ -1,5 +1,6 @@
package com.kassaev.notes.config
import com.kassaev.notes.repository.IUserRepository
import com.kassaev.notes.repository.UserRepository
import com.kassaev.notes.service.CustomUserDetailsService
import org.springframework.boot.context.properties.EnableConfigurationProperties
@ -18,14 +19,14 @@ import org.springframework.security.crypto.password.PasswordEncoder
class Configuration {
@Bean
fun userDetailsService(userRepository: UserRepository): UserDetailsService =
fun userDetailsService(userRepository: IUserRepository): UserDetailsService =
CustomUserDetailsService(userRepository)
@Bean
fun encoder(): PasswordEncoder = BCryptPasswordEncoder()
@Bean
fun authenticationProvider(userRepository: UserRepository): AuthenticationProvider =
fun authenticationProvider(userRepository: IUserRepository): AuthenticationProvider =
DaoAuthenticationProvider()
.also {
it.setUserDetailsService(userDetailsService(userRepository))

View File

@ -18,7 +18,7 @@ class AuthController(
fun authenticate(@RequestBody authRequest: AuthenticationRequest): AuthenticationResponse =
authenticationService.authentication(authRequest)
@PostMapping("/refresh")
@PostMapping("/refresh_old")
fun refreshAccessToken(
@RequestBody request: RefreshTokenRequest
): TokenResponse =
@ -26,6 +26,14 @@ class AuthController(
?.mapToTokenResponse()
?: throw ResponseStatusException(HttpStatus.FORBIDDEN, "Invalid refresh token!")
@PostMapping("/refresh")
fun refreshAccessToken2(
@RequestBody request: RefreshTokenRequest
): TokenResponse =
authenticationService.refreshAccessToken2(request.token)
?.mapToTokenResponse()
?: throw ResponseStatusException(HttpStatus.FORBIDDEN, "Invalid refresh token!")
private fun String.mapToTokenResponse(): TokenResponse =
TokenResponse(
token = this

View File

@ -7,10 +7,12 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.util.*
@RestController
@RequestMapping("/api/v1.0.0/note")
@ -25,8 +27,13 @@ class NoteController(val service: NoteService) {
return service.getAllNotes()
}
@PostMapping("/create")
fun createNote(@RequestBody note: Note): Note {
return service.updateNote(note)
}
@GetMapping("/{id}")
fun getNoteById(@PathVariable id: Int): Note? {
fun getNoteById(@PathVariable id: Long): Optional<Note> {
return service.getNoteById(id)
}
@ -36,7 +43,7 @@ class NoteController(val service: NoteService) {
}
@DeleteMapping("/remove/{id}")
fun deleteNote(@PathVariable id: Int){
fun deleteNote(@PathVariable id: Long){
return service.deleteNote(id)
}
}

View File

@ -3,8 +3,9 @@ package com.kassaev.notes.controller.user
import com.kassaev.notes.model.Role
import com.kassaev.notes.model.User
import com.kassaev.notes.service.UserService
import org.springframework.data.jpa.domain.AbstractPersistable_.id
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
@ -13,12 +14,12 @@ import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException
import java.util.UUID
@RestController
@RequestMapping("/api/v1.0.0/user")
class UserController(
private val userService: UserService
private val userService: UserService,
private val encoder: PasswordEncoder
) {
@PostMapping
@ -34,34 +35,32 @@ class UserController(
userService.findAll()
.map { it.toResponse() }
@GetMapping("/{uuid}")
fun findByUUID(@PathVariable uuid: UUID): UserResponse =
userService.findByUUID(uuid)
?.toResponse()
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Cannot find a user.")
@DeleteMapping("/{uuid}")
fun deleteByUUID(@PathVariable uuid: UUID): ResponseEntity<Boolean> {
val success = userService.deleteByUUID(uuid)
return if(success) {
ResponseEntity.noContent().build()
} else {
@GetMapping("/{id}")
fun findById(@PathVariable id: Long): UserResponse {
return if (userService.findById(id).isPresent){
val user = userService.findById(id).get()
user.toResponse()
}else{
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Cannot find a user.")
}
}
@DeleteMapping("/{id}")
fun deleteById(@PathVariable id: Long){
userService.deleteById(id)
}
private fun UserRequest.toModel(): User =
User(
id = UUID.randomUUID(),
id = null,
email = this.email,
password = this.password,
password = encoder.encode(this.password),
role = Role.USER
)
private fun User.toResponse(): UserResponse =
UserResponse(
uuid = this.id,
id = this.id,
email = this.email
)
}

View File

@ -1,8 +1,6 @@
package com.kassaev.notes.controller.user
import java.util.UUID
data class UserResponse(
val uuid: UUID,
val id: Long?,
val email: String
)

View File

@ -1,7 +1,13 @@
package com.kassaev.notes.model
import jakarta.persistence.*
@Entity
@Table(name = "notes")
data class Note(
val id: Int,
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long?,
val text: String,
val dateCreated: String
)

View File

@ -0,0 +1,14 @@
package com.kassaev.notes.model
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import org.springframework.security.core.userdetails.UserDetails
@Entity
@Table(name = "refresh_tokens")
data class RefreshToken(
@Id
val email: String,
val token: String
)

View File

@ -1,11 +1,16 @@
package com.kassaev.notes.model
import java.util.UUID
import jakarta.persistence.*
@Entity
@Table(name="users")
data class User(
val id: UUID,
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long?,
val email: String,
val password: String,
@Enumerated(EnumType.STRING)
val role: Role
)

View File

@ -0,0 +1,8 @@
package com.kassaev.notes.repository
import com.kassaev.notes.model.Note
import org.springframework.data.jpa.repository.JpaRepository
interface INoteRepository: JpaRepository<Note, Long> {
}

View File

@ -0,0 +1,11 @@
package com.kassaev.notes.repository
import com.kassaev.notes.model.RefreshToken
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
interface IRefreshTokenRepository: JpaRepository<RefreshToken, Long> {
@Query("SELECT * FROM refresh_tokens WHERE email = :email", nativeQuery = true)
fun findByEmail(@Param("email") email: String): RefreshToken?
}

View File

@ -0,0 +1,15 @@
package com.kassaev.notes.repository
import com.kassaev.notes.model.User
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import java.awt.print.Book
import java.time.LocalDate
interface IUserRepository: JpaRepository<User, Long> {
@Query("SELECT * FROM users WHERE email = :email", nativeQuery = true)
fun findByEmail(@Param("email") email: String): User?
}

View File

@ -1,33 +0,0 @@
package com.kassaev.notes.repository
import com.kassaev.notes.model.Note
import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service
import java.util.*
@Repository
class MockRepository {
private val notesList = mutableListOf(
Note(id = 0, text = "first", dateCreated = "21:15"),
Note(id = 1, text = "second", dateCreated = "22:15"),
Note(id = 2, text = "third", dateCreated = "23:15"),
Note(id = 3, text = "fourth", dateCreated = "00:15"),
)
fun getAllNotes(): List<Note>{
return notesList
}
fun getNoteById(id: Int): Note? {
return notesList.find{ it.id == id}
}
fun updateNote(note: Note): Note {
notesList[note.id] = note
return note
}
fun deleteNote(id: Int){
notesList.removeIf { it.id == id }
}
}

View File

@ -4,7 +4,6 @@ import com.kassaev.notes.model.Role
import com.kassaev.notes.model.User
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Repository
import java.util.*
@Repository
class UserRepository(
@ -12,19 +11,19 @@ class UserRepository(
) {
private val users = mutableListOf(
User(
id = UUID.randomUUID(),
id = 1,
email = "user1@mail.com",
password = encoder.encode("pass1"),
role = Role.USER
),
User(
id = UUID.randomUUID(),
id = 2,
email = "user2@mail.com",
password = encoder.encode("pass2"),
role = Role.USER
),
User(
id = UUID.randomUUID(),
id = 3,
email = "user3@mail.com",
password = encoder.encode("pass3"),
role = Role.ADMIN
@ -41,14 +40,14 @@ class UserRepository(
fun findByEmail(email: String): User? =
users.firstOrNull { it.email == email }
fun findByUUID(uuid: UUID): User? =
users.firstOrNull { it.id == uuid }
fun findById(id: Long): User? =
users.firstOrNull { it.id == id }
fun findAll(): List<User> =
users
fun deleteByUUID(uuid: UUID): Boolean {
val foundUser = findByUUID(uuid)
fun deleteById(id: Long): Boolean {
val foundUser = findById(id)
return foundUser?.let {
users.remove(it)

View File

@ -3,6 +3,8 @@ package com.kassaev.notes.service
import com.kassaev.notes.config.JwtProperties
import com.kassaev.notes.controller.auth.AuthenticationRequest
import com.kassaev.notes.controller.auth.AuthenticationResponse
import com.kassaev.notes.model.RefreshToken
import com.kassaev.notes.repository.IRefreshTokenRepository
import com.kassaev.notes.repository.RefreshTokenRepository
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
@ -16,7 +18,8 @@ class AuthenticationService(
private val userDetailsService: CustomUserDetailsService,
private val tokenService: TokenService,
private val jwtProperties: JwtProperties,
private val refreshTokenRepository: RefreshTokenRepository
private val refreshTokenRepository: RefreshTokenRepository,
private val refreshTokenRepository2: IRefreshTokenRepository
) {
fun authentication(authRequest: AuthenticationRequest): AuthenticationResponse {
authManager.authenticate(
@ -31,7 +34,13 @@ class AuthenticationService(
val accessToken = generateAccessToken(user)
val refreshToken = generateRefreshToken(user)
refreshTokenRepository.save(refreshToken, user)
refreshTokenRepository2.save(
RefreshToken(
email = user.username,
token = refreshToken
)
)
// refreshTokenRepository2.save(refreshToken, user)
return AuthenticationResponse(
accessToken = accessToken,
@ -63,5 +72,40 @@ class AuthenticationService(
null
}
}
fun refreshAccessToken2(token: String): String? {
val extractedEmail = tokenService.extractEmail(token)
println()
println(extractedEmail)
println()
return extractedEmail?.let { email ->
val currentUserDetails = userDetailsService.loadUserByUsername(email)
println()
println("AAAAAAAAAAAAAAAA")
println(currentUserDetails.username)
println()
val refreshTokenInDB = refreshTokenRepository2.findByEmail(currentUserDetails.username)
// val refreshTokenUserDetails = refreshTokenRepository2.findUserDetailsByToken(token)
println()
println("BBBBBBBBBBB")
println(refreshTokenInDB?.email)
println()
if (!tokenService.isExpired(token) && currentUserDetails.username == refreshTokenInDB?.email){
// TODO: check if this refresh token is the same token in db for this user <refresh token, user>. If so generate new access token and refresh token and update record in with newly created refresh token for this user.
println()
println("PPPPPPPPPPPPPPPPPPAAAAAAAAAAAAAAAASSSSSSSSSSSSSSSEEEEEEEEEEEEEEEDDDDDDDDDDDD!!!")
println()
generateAccessToken(currentUserDetails)
} else
null
}
}
}

View File

@ -1,6 +1,6 @@
package com.kassaev.notes.service
import com.kassaev.notes.repository.UserRepository
import com.kassaev.notes.repository.IUserRepository
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
@ -10,7 +10,7 @@ import org.springframework.stereotype.Service
typealias ApplicationUser = com.kassaev.notes.model.User
@Service
class CustomUserDetailsService(
private val userRepository: UserRepository
private val userRepository: IUserRepository
): UserDetailsService {
override fun loadUserByUsername(username: String): UserDetails =
userRepository.findByEmail(username)

View File

@ -1,30 +1,31 @@
package com.kassaev.notes.service
import com.kassaev.notes.model.Note
import com.kassaev.notes.repository.MockRepository
import lombok.AllArgsConstructor
import org.springframework.beans.factory.annotation.Autowired
//import com.kassaev.notes.repository.MockRepository
import com.kassaev.notes.repository.INoteRepository
import org.springframework.stereotype.Service
import java.util.*
@Service
class NoteService(val repository: MockRepository) {
// @Autowired
// lateinit var repository: MockRepository
class NoteService(val repository: INoteRepository) {
fun getAllNotes(): List<Note>{
return repository.getAllNotes()
return repository.findAll()
// return repository.getAllNotes()
}
fun getNoteById(id: Int): Note? {
return repository.getNoteById(id)
fun getNoteById(id: Long): Optional<Note> {
return repository.findById(id)
// return repository.getNoteById(id)
}
fun updateNote(note: Note): Note {
return repository.updateNote(note)
return repository.save(note)
// return repository.updateNote(note)
}
fun deleteNote(id: Int){
repository.deleteNote(id)
fun deleteNote(id: Long){
repository.deleteById(id)
// repository.deleteNote(id)
}
}

View File

@ -12,7 +12,11 @@ import java.util.Date
class TokenService(
jwtProperties: JwtProperties
) {
val sekret = jwtProperties.key
private val secretKey = Keys.hmacShaKeyFor(
jwtProperties.key.toByteArray()
)

View File

@ -1,13 +1,14 @@
package com.kassaev.notes.service
import com.kassaev.notes.model.User
import com.kassaev.notes.repository.UserRepository
import com.kassaev.notes.repository.IUserRepository
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import java.util.UUID
import java.util.*
@Service
class UserService(
private val userRepository: UserRepository
private val userRepository: IUserRepository
) {
fun createUser(user: User): User? {
val found = userRepository.findByEmail(user.email)
@ -18,13 +19,13 @@ class UserService(
} else null
}
fun findByUUID(uuid: UUID): User? =
userRepository.findByUUID(uuid)
fun findById(id: Long): Optional<User> =
userRepository.findById(id)
fun findAll(): List<User> =
userRepository.findAll()
fun deleteByUUID(uuid: UUID): Boolean =
userRepository.deleteByUUID(uuid)
fun deleteById(id: Long) =
userRepository.deleteById(id)
}

View File

@ -1,4 +1,15 @@
jwt:
key: ${JWT_KEY}
access-token-expiration: 3600000
refresh-token-expiration: 86400000
access-token-expiration: ${JWT_ACCESS_EXP}
refresh-token-expiration: ${JWT_REFRESH_EXP}
spring:
datasource:
driver-class-name: ${DB_DRIVER}
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
hibernate:
ddl-auto: update
server:
port: ${SPRING_PORT}

View File

@ -1,13 +0,0 @@
package com.kassaev.notes
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest
class NotesApplicationTests {
@Test
fun contextLoads() {
}
}