Securing Your App with Spring Security

Paul Woods

April 6th, 2016

mr.paul.woods@gmail.com

https://github.com/paulwoods/employeedb

What Were About to Do

This talk shows how to take an application (Spring Boot + Gradle + Thymeleaf + Spring Data JPA) and add spring security to it.

Demo Unsecured Application

We have a HR application called EmployeeDB. It allows us to maintain our employees, thier profiles (addresses) and thier salaries. It currently has no authentication or authorization.

You can clone the source code and follow along.

https://github.com/paulwoods/employeedb.git

Run the project in the 1-initial folder

Add Security Starter

Our first step is to add the Spring Security dependencies to our project.

Following Along?

  • Start with the project in the 1-initial folder.
  • When were done, we'll have the project in the 2-springstarter folder.

Add Security Starter

  1. Add the spring security starter dependency.
    build.gradle
    						
    ...
    dependencies {
        ...
        compile('org.springframework.boot:spring-boot-starter-security')
        ...
    }
    ...
    					
  2. Refresh your IDE's gradle dependencies.
  3. Run the application.

Add Security Starter

Adding this starter adds spring security to the classpath of the application, and configures it with default settings. When the application is accessed, the user will be aksed for the user and password. The default user is 'user'. The password is a hash, and it can be found in the logs file. This password is different each time the application is started.
console output

2016-03-14 19:34:09.256  INFO 11952 --- [ost-startStop-1] b.a.s.AuthenticationManagerConfiguration :

Using default security password: 16a01371-b881-4a40-b9f7-288e3e4ee7b2

2016-03-14 19:34:09.469  INFO 11952 --- [ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/css/**'], Ant [pattern='/js/**'], Ant [pattern='/images/**'], Ant [pattern='/**/favicon.ico'], Ant [pattern='/error']]], []

					

Add Security Starter

  • Our app is now secured
  • The users must know the user name and password to access any page of the application
  • The password changes every time the application is ran.

Set the Default Username and Password

Having a single user name for all users is a bad practice. And looking through the logs for the password is time consuming and annoying. We can solve this by change the user name, and setting the password to a known value.

Following Along?

  • Start with the project in the 2-springstarter folder.
  • When were done, we'll have the project in the 3-singleuser folder.

Set the Default Username and Password

  1. Edit the application.properties file
  2. Set security.user.name to the username
  3. Set security.user.password to the password.

src\main\resources\application.properties
...
security.user.name=paul
security.user.password=123456
...
					

Set the Default Username and Password

Now, when you run the application, and it asks for the user and password, enter paul and 123456 to login.

Of course, there is only one account. You probably need to store multiple users.

Users, Authorities, and Roles

We are goinging to defines the domains / tables to store our users, and map the roles they are granted.

Following Along?

  • Start with the project in the 3-singleuser folder.
  • When were done, we'll have the project in the 4-userdomain folder.

Users, Authorities, and Roles

  1. Edit the application.properties file
  2. Delete the security.user.name line (if it exists)
  3. Delete the security.user.password line (if it exists).

Users, Authorities, and Roles

  1. Create <base package>.security pacakge
  2. Create Role enum
  3. Create User class, impelmenting UserDetail
  4. Create Authority class, implementing GrantedAuthority

Users, Authorities, and Roles

src\main\groovy\org\mrpaulwoods\employeedb\security\Role.groovy
package org.mrpaulwoods.employee.security

enum Role {
    ROLE_USER,
    ROLE_ADMIN
}

Users, Authorities, and Roles

src\main\groovy\org\mrpaulwoods\employeedb\security\User.groovypackage org.mrpaulwoods.employee.security

import org.springframework.security.core.userdetails.UserDetails
import javax.persistence.*

@Entity
class User implements UserDetails {
    @Id @GeneratedValue Long id
    @Column(unique=true) String username
    String password
    boolean enabled = true
    boolean accountNonExpired = true
    boolean accountNonLocked = true
    boolean credentialsNonExpired = true

    @OneToMany(mappedBy = "user", fetch=FetchType.EAGER, cascade = CascadeType.ALL)
    List<Authority> authorities = []
}

Users, Authorities, and Roles

src\main\groovy\org\mrpaulwoods\employeedb\security\Authority.groovypackage org.mrpaulwoods.employee.security
import org.springframework.security.core.GrantedAuthority
import javax.persistence.*

@Entity 
class Authority implements GrantedAuthority {

    @Id @GeneratedValue 
    Long id

    @ManyToOne @JoinColumn(name="user_Id", nullable=false) 
    User user

    String authority

}

Users, Authorities, and Roles

src\main\groovy\org\mrpaulwoods\employeedb\security\EmployeeUserDetailsService.groovypackage org.mrpaulwoods.employee.security

import groovy.util.logging.Slf4j
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.stereotype.Service

import javax.annotation.PostConstruct

@Service
class EmployeeUserDetailsService implements UserDetailsService {

    @Autowired UserRepository userRepository

    @Autowired AuthorityRepository authorityRepository

    @Override UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
        if(user)
        	user
        else
        	throw new UsernameNotFoundException(username)
    }

    @PostConstruct void init() {
        User user = userRepository.save(new User(username:"paulwoods", password:"alpha"))
        user.authorities << authorityRepository.save(new Authority(user:user, authority: "ROLE_USER"))
        user.authorities << authorityRepository.save(new Authority(user:user, authority: "ROLE_ADMIN"))
    }

}

Users, Authorities, and Roles

src\main\groovy\org\mrpaulwoods\employeedb\security\SecurityConfig.groovypackage org.mrpaulwoods.employee.security

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter

@Configuration
@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    EmployeeUserDetailsService employeeUserDetailsService

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.userDetailsService(employeeUserDetailsService)
    }

    @Override
    protected void configure(HttpSecurity http) {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/css/**").permitAll()
                .antMatchers("/js/**").permitAll()
                .antMatchers("/images/**").permitAll()
                .antMatchers("/login/**").permitAll()

                .anyRequest().authenticated()
                .and()

            // configure the login form

                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()

            // configure the logout form

                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("http://www.google.com") // TODO: change url to "/" for production
                .permitAll()
                .and()

    }

}

Users, Authorities, and Roles

src\main\groovy\org\mrpaulwoods\employeedb\security\AuthorityRepository.groovypackage org.mrpaulwoods.employee.security

import org.springframework.data.jpa.repository.JpaRepository

interface AuthorityRepository extends JpaRepository<Authority, Long> {

}

Users, Authorities, and Roles

src\main\groovy\org\mrpaulwoods\employeedb\security\UserRepository.groovypackage org.mrpaulwoods.employee.security

import org.springframework.data.jpa.repository.JpaRepository

interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username)
}

Users, Authorities, and Roles

src\main\groovy\org\mrpaulwoods\employeedb\EmployeeApplication.groovypackage org.mrpaulwoods.employee

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter

@SpringBootApplication
class EmployeeApplication extends WebMvcConfigurerAdapter {

	static void main(String[] args) {
		SpringApplication.run EmployeeApplication, args
	}

	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/login").setViewName("login")
		registry.addViewController("/logout").setViewName("logout")
	}

}

Users, Authorities, and Roles

src\main\resources\templates\login.groovy<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">

<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      xmlns="http://www.w3.org/1999/xhtml"
      layout:decorator="employee">

<body>

<div layout:fragment="body">

    <div th:if="${param.error}">
        Invalid username and password.
    </div>
    <div th:if="${param.logout}">
        You have been logged out.
    </div>
    <form th:action="@{/login}" method="post">
        <div class="form-group">
            <label>User Name:</label>
            <input class="form-control" type="text" name="username"/>
        </div>
        <div class="form-group">
            <label>Password:</label>
            <input class="form-control" type="password" name="password"/>
        </div>
        <div><input type="submit" value="Sign In"/></div>
    </form>

</div>
</body>

</html>

Encrypting Passwords

If you were to look at the user database table, then you would see that the passwords are stored in plain text, and need to be stored encrypted instead. We will add the BCrypt encryption library (already included by spring), as well as update the authentication manager to use the new encrypted passwords.

Following Along?

  • Start with the project in the 4-userdomain folder.
  • When were done, we'll have the project in the 5-encrypt-password folder.

Encrypting Passwords

  1. Modify EmployeeUserDetailsService.groovy
...
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
...
@PostConstruct
void init() {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder()

        User user = userRepository.save(new User(username: "paulwoods", 
        	password: encoder.encode("beta")))
        user.authorities << authorityRepository.save(
        	new Authority(user: user, authority: "ROLE_USER"))
        user.authorities << authorityRepository.save(
        	new Authority(user: user, authority: "ROLE_ADMIN"))
}
...

Encrypting Passwords

  1. Update SecurityConfig to use BCrypt as the password encoder.
        @Override
        protected void configure(AuthenticationManagerBuilder auth) {
            auth
                    .userDetailsService(employeeUserDetailsService)
                    .passwordEncoder(new BCryptPasswordEncoder())
        }
    

Accessing the Principal

The currently logged-in user is called the principal. You can access the principal and get thier name.

Following Along?

  • Start with the project in the 5-encrypt-password folder.

Accessing the Principal

From a Controller

  1. Add a java.security.Principal parameter to the controller method.
  2. Access the getName() method to get the user name.
  3. src\main\groovy\org\mrpaulwoods\employee\home\HomeController.groovy					
        @RequestMapping(method=GET)
        String index(Principal principal) {
            println "principal = $principal?.name"
            "home/index"
        }
    

Accessing the Principal

From a Service

This example accesses the principal from a Service.

src\main\groovy\org\mrpaulwoods\employee\home\HomeService.groovy
...
import org.springframework.security.core.context.SecurityContextHolder

@Slf4j @Service @Transactional class HomeService {
    void example() {
        def principal = SecurityContextHolder.context?.authentication
        log.info "user = $principal?.name"
    }
}
					

Accessing the Principal

From a Thymeleaf Template

This example accesses the principal from a Thymeleaf template.

src\main\resources\templates\employee.html
<ul class="nav navbar-nav navbar-right">
    <li>
        <a href="#" th:text="'Welcome, ' + ${#authentication.name}">Name</a>
    </li>
</ul>
					

Manually Loggin-in

In some situations, you have a User object (that extends UserDetail), and you want that use to become the currently logged in user. Add these lines to set the logged-in user.


import org.springframework.security.core.Authentication
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder

...

UserDetail user = ...

Authentication authentication = new UsernamePasswordAuthenticationToken(
	user, user.password, user.authorities)
SecurityContextHolder.context.authentication = authentication
				

Authorization

Authorization determines what the user can an cannot access. We set this up by giving each user a 1-or-more roles that they can do, then we configure the URLs and/or controllers to allow access based on the roles.

Following Along?

  • Start with the project in the 5-encrypt-password folder.

Authorization

We've specified the URL based authorization in the SecurityConfig

src\main\groovy\org\mrpaulwoods\employeedb\security\SecurityConfig.groovy
.antMatchers("/webjars/**").permitAll()
.antMatchers("/css/**").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/images/**").permitAll()
.antMatchers("/login/**").permitAll()

.anyRequest().authenticated()
				
  • The 5 urls are authorized for anyone to use.
  • Any other requests require an authenticated user.

Authorization

We can also specify class and method level authorizations

  • Use the @Secured annotation
  • @Secured("ROLE_TELLER")
    
  • @PreAuthorize with a SPEL expression
  • @PreAuthorize("hasAuthority('ROLE_TELLER')")
    

Remember, to add @EnableGlobalMethodSecurity(securedEnabled = true) to your SecurityConfig class

@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)

Following Along?

  • Check out the tag encrypted-password.

We will only allow users with role ROLE_HR to view the payroll data.

  • Edit SecurityConfig.groovy.
  • Add the annotation @EnableGlobalMethodSecurity to the class.
src\main\groovy\org\mrpaulwoods\employee\payroll\PayrollController.groovy...
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
...
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfig extends WebSecurityConfigurerAdapter {
...
  • Edit PayrollController.groovy.
  • Add @PreAuthorize('hasRole("ROLE_HR")') to the class
src\main\groovy\org\mrpaulwoods\employee\payroll\PayrollController.groovy...
import org.springframework.security.access.prepost.PreAuthorize
...
@Controller
@RequestMapping(value = "/payroll")
@Slf4j
@PreAuthorize('hasRole("ROLE_HR")')
class PayrollController {
...
  • Now, users with the role ROLE_HR will see the page. Other users will get a 403 forbidden error message.

Securing Restfull Endpoints

We'll start with a un-secured api endpoint. We'll use JWT (https://jwt.io/) to create auth tokens on the server. We'll create a login api endpoint, which will take the user/pass and return the token. The client will send the token in the header of each subsequent call.

Following Along?

  • We don't have a starting project for this one. See the 7-rest folder for the final project.

First, Build an un-secured API

  • Edit the SecurityConfig.groovy file.
  • Add .antMatchers("/api/**").permitAll()
src\main\groovy\org\mrpaulwoods\employee\security\SecurityConfig.groovy...
protected void configure(HttpSecurity http) {
    http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/webjars/**").permitAll()
            .antMatchers("/css/**").permitAll()
            .antMatchers("/js/**").permitAll()
            .antMatchers("/images/**").permitAll()
            .antMatchers("/login/**").permitAll()
            .antMatchers("/api/**").permitAll()

            .anyRequest().authenticated()
            .and()

...

Add an API Controller

  • Create the ApiController.groovy file.
src\main\groovy\org\mrpaulwoods\employee\api\ApiController.groovypackage org.mrpaulwoods.employee.api

import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping(value="/api/1")
class ApiController {

    @RequestMapping(value="/home")
    Message home() {
        new Message()
    }
}

Add an API Controller

  • Create the Message.groovy file.
src\main\groovy\org\mrpaulwoods\employee\api\Message.groovypackage org.mrpaulwoods.employee.api

class Message {
    String message = "hello, world"
}

Test the API

  • Access localhost:8080/employeedb/api/1/home.
  • It returns {message: "hello, world"}

Install JJWT

  • Add a the jjwt dependency to your build file.
  • compile('io.jsonwebtoken:jjwt:0.6.0')

Update SecurityConfig.groovy

src\main\groovy\org\mrpaulwoods\employeedb\security\SecurityConfig.groovy
...
import org.mrpaulwoods.employee.security.jwt.StatelessAuthenticationFilter
import org.mrpaulwoods.employee.security.jwt.StatelessLoginFilter
import org.mrpaulwoods.employee.security.jwt.TokenAuthenticationService
...
@Autowired
private TokenAuthenticationService tokenAuthenticationService
...

    protected void configure(HttpSecurity http) {

// configure api security

	.addFilterBefore(
	new StatelessLoginFilter("/api/login", tokenAuthenticationService,
		employeeUserDetailsService, authenticationManager()),
		UsernamePasswordAuthenticationFilter)

	.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService), 
		UsernamePasswordAuthenticationFilter)
}
...

Add the token's secret phrase

src\main\resources\application.properties
token.secret=the-secret-phrase-goes-here

Create StatelessAuthenticationFilter

src\main\groovy\org\mrpaulwoods\employee\security\jwt\StatelessAuthenticationFilter.groovy
package org.mrpaulwoods.employee.security.jwt

import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.filter.GenericFilterBean
import javax.servlet.FilterChain
import javax.servlet.ServletException
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse

class StatelessAuthenticationFilter extends GenericFilterBean {

    private final TokenAuthenticationService tokenAuthenticationService

    public StatelessAuthenticationFilter(TokenAuthenticationService taService) {
        this.tokenAuthenticationService = taService
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        SecurityContextHolder.context.authentication = tokenAuthenticationService.getAuthentication(req)
        chain.doFilter req, res
    }

}

Create StatelessLoginFilter

src\main\groovy\org\mrpaulwoods\employee\security\jwt\StatelessLoginFilter.groovy
package org.mrpaulwoods.employee.security.jwt

import com.fasterxml.jackson.databind.ObjectMapper
import groovy.util.logging.Slf4j
import org.mrpaulwoods.employee.security.User
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter
import org.springframework.security.web.util.matcher.AntPathRequestMatcher

import javax.servlet.FilterChain
import javax.servlet.ServletException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

@Slf4j
class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter {

    private final TokenAuthenticationService tokenAuthenticationService
    private final UserDetailsService userDetailsService

    protected StatelessLoginFilter(String urlMapping,
                                   TokenAuthenticationService tokenAuthenticationService,
                                   UserDetailsService userDetailsService,
                                   AuthenticationManager authManager) {
        super(new AntPathRequestMatcher(urlMapping))
        this.userDetailsService = userDetailsService
        this.tokenAuthenticationService = tokenAuthenticationService
        setAuthenticationManager(authManager)
    }

    /** Parses the {username: "xxx", password:"yyy"} json string, and attempts to login **/

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {

        final User user = new ObjectMapper().readValue(request.getInputStream(), User.class)

        final UsernamePasswordAuthenticationToken loginToken = new UsernamePasswordAuthenticationToken(
                user.username, user.password)

        return getAuthenticationManager().authenticate(loginToken)
    }

    /** the login was successful. create the token **/

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain chain, Authentication authentication) throws IOException, ServletException {

        // Lookup the complete User object from the database and create an Authentication for it
        final User authenticatedUser = userDetailsService.loadUserByUsername(authentication.getName())
        final UserAuthentication userAuthentication = new UserAuthentication(authenticatedUser)

        // Add the custom token as HTTP header to the response
        tokenAuthenticationService.addAuthentication(response, userAuthentication)

        // Add the authentication to the Security context
        SecurityContextHolder.getContext().setAuthentication(userAuthentication)
    }
}

Create TokenAuthenticationService

src\main\groovy\org\mrpaulwoods\employee\security\jwt\TokenAuthenticationService.groovy
package org.mrpaulwoods.employee.security.jwt

import groovy.util.logging.Slf4j
import org.mrpaulwoods.employee.security.EmployeeUserDetailsService
import org.mrpaulwoods.employee.security.User
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.security.core.Authentication
import org.springframework.stereotype.Service

import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

@Service
@Slf4j
public class TokenAuthenticationService {

    private static final String AUTH_HEADER_NAME = "X-AUTH-TOKEN"

    @Autowired
    UserTokenService userTokenService

    /** adds the current user's token to the header */

    public void addAuthentication(HttpServletResponse response, UserAuthentication authentication) {
        final User user = authentication.getDetails()
        response.addHeader(AUTH_HEADER_NAME, userTokenService.userToToken(user))
    }

    /** retrieves the current user from the token and stores in authentication object */

    public Authentication getAuthentication(HttpServletRequest request) {
        final String token = request.getHeader(AUTH_HEADER_NAME)

        if (!token) {
            return null
        }

        final User user = userTokenService.tokenToUser(token)
        if (!user) {
            return null
        }

        return new UserAuthentication(user)
    }

}

Create UserAuthentication

src\main\groovy\org\mrpaulwoods\employee\security\jwt\UserAuthentication.groovy
package org.mrpaulwoods.employee.security.jwt

import org.mrpaulwoods.employee.security.User
import org.springframework.security.core.Authentication
import org.springframework.security.core.GrantedAuthority

public class UserAuthentication implements Authentication {

    private final User user
    private boolean authenticated = true

    public UserAuthentication(User user) {
        this.user = user
    }

    @Override
    public String getName() {
        return user.getUsername()
    }

    @Override
    public Collection getAuthorities() {
        return user.getAuthorities()
    }

    @Override
    public Object getCredentials() {
        return user.getPassword()
    }

    @Override
    public User getDetails() {
        return user
    }

    @Override
    public Object getPrincipal() {
        return user.getUsername()
    }

    @Override
    public boolean isAuthenticated() {
        return authenticated
    }

    @Override
    public void setAuthenticated(boolean authenticated) {
        this.authenticated = authenticated
    }

}


Create UserTokenService

src\main\groovy\org\mrpaulwoods\employee\security\jwt\UserTokenService.groovy
package org.mrpaulwoods.employee.security.jwt

import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import org.mrpaulwoods.employee.security.EmployeeUserDetailsService
import org.mrpaulwoods.employee.security.User
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service

/**
 * This class converts between Users and JWT Tokens.
 */
@Service
public class UserTokenService {

    @Autowired
    EmployeeUserDetailsService employeeUserDetailsService

    @Value(value = '${token.secret}')
    String secret

    User tokenToUser(String token) {
        assert secret

        String username = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody()
                .getSubject()

        employeeUserDetailsService.loadUserByUsername username
    }

    String userToToken(User user) {
        assert secret

        Jwts.builder()
                .setSubject(user.username)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact()
    }

}

Login to get the API Token

  • POST: http://localhost:8080/employeedb/api/login
    body: {"username":"paulwoods","password":"beta"}
  • Returns a 200 on a good login. Pull the token from the header
    X-AUTH-TOKEN : eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJwYXVsd29vZHMifQ.ep6P2L0WUVd1KShY_N1f2Rw-suG7A71IhuvpQ0weeYbic9ZVGzgISrJ3oGlyJdsKvQYJs_lgIL-zYTiw0nfbkQ

Access the API, using the token

  • GET: http://localhost:8080/employeedb/api/1/home
    add header: X-AUTH-TOKEN : eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJwYXVsd29vZHMifQ.ep6P2L0WUVd1KShY_N1f2Rw-suG7A71IhuvpQ0weeYbic9ZVGzgISrJ3oGlyJdsKvQYJs_lgIL-zYTiw0nfbkQ
  • Returns a 200
    body: {"message":"hello, world"}

Oauth2 Login

See the Spring Guide:

https://spring.io/guides/tutorials/spring-boot-oauth2/