Creating a JWT secured react app and Kotlin server (part 2)

Time for some code at last! If you haven’t read my last post, please head on back here.

I’m starting things off with a very simple controller that returns HTML responses.

package com.chrisyoung.auth

import org.springframework.http.MediaType
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.ui.set
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam


@Controller
class LoginController {
    @GetMapping("/login")
    fun loginForm(model: Model): String {
        model["title"] = "Login"
        return "login"
    }
    @PostMapping("/login", consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE])
    fun login(
            model: Model,
            @RequestParam(name = "username") username: String,
            @RequestParam(name = "password") password: String
    ): String {
        model["title"] = "Login"
        model["username"] = username;
        model["password"] = password;
        return "loggedin"
    }
}

Annotations are extremely powerful inĀ Spring Boot. The @Controller, @GetMapping and @PostMapping annotations are taking care of the routing and request and response handling for us. I admit that they do seem a little bitĀ too magical, but I’ve always enjoyed a bit of magic over a bunch of boilerplate.

in the functions we can see the beautiful strictly typed parameters, including the special parameter “Model” used by the mustache templates below

The application is very simple at this point. The /login route displays a html form with fields for username and password, and when that is submitted, those values are pulled out of the form-encoded request into String-type function parameters which are just displayed on the next page. I’m not actually implementing any login logic here, just request logic. I haven’t started with REST endpoints yet, because I need a couple of actual HTML pages in my auth service for the initial login and the authorisation page.

I also need a couple of templates named to match the return values of the GET and POST mapping above

{{>_header}}
    <h1>Login</h1>
    <form method="POST" action="/login">
        username:
        <input type="text" name="username"/>
        <br/>
        password:
        <input type="password" name="password"/>
        <br/>
        <input type="submit" title="login"/>
    </form>
{{>_footer}}

/resources/templates/login.mustache

{{>_header}}
<h1>Logged in</h1>
<p>username {{username}}</p>
<p>password {{password}}</p>
{{>_footer}}

/resources/templates/loggedin.mustache

Next I’ll add a bit of logic to make it a bit more functional

First I’ll add a User entity: (from here on out I’ll exclude the imports)

@Entity
class User(
        var username: String,
        var password: String,
        @Id @GeneratedValue var id: Long? = null)

Entities.kt

and a repository interface (I’m not reinventing the wheel here, the Spring Framework’s CrudRepository class will do all the real work)

package com.chrisyoung.auth

import org.springframework.data.repository.CrudRepository

interface UserRepository : CrudRepository<User, Long> {
    fun findByUsername(username: String): User?
}

Repositories.kt

And a neat little test

@DataJpaTest
class RepositoriesTest @Autowired constructor(
        val entityManager: TestEntityManager,
        val userRepository: UserRepository
) {
    @Test
    fun `When findByLogin then return user`() {
        val oldMate = User("oldmate", "password1")
        entityManager.persist(oldMate)
        entityManager.flush()
        val user = userRepository.findByUsername("oldmate")
        assertThat(user).isEqualTo(oldMate)
    }
}

And I’ll access that in the controller simply like this:

    fun login(
            model: Model,
            request: HttpServletRequest,
            @RequestParam(name = "username") username: String,
            @RequestParam(name = "password") password: String
    ): String {
        model["title"] = "Login"
        model["error"] = ""
        model["username"] = username;
        model["password"] = password;
        val user = userRepository.findByUsername(username)
        if (user !== null && password == user.password) {
            val session = request.session
            session.setAttribute("username", username)
            session.setAttribute("user", user)
            return "loggedin"
        } else {
            model["error"] = "user not found or password incorrect"
        }
        return "login"
    }

I also added the request object as a function parameter, and from it got the “session”, where I’ll store the user data, so they won’t have to log in again until the session expires or the server re-starts. There’s ways to persist the session in persistent storage like redis, but I’m not interested in that here.

That’s all for now. Next time I’ll expand the controllers a little, and switch from Maven to Gradle.

Comments Closed

Comments are closed.