Looking to add some security to your Spring Boot application? If so, then you should consider using a JSON Web Token (JWT).

If you're unfamiliar with JWT, it's a nice and neat way to securely transmit info between a client and server. It does that with the aid of a token in the request header.

How does the token contribute to security? It's digitally signed. That signature verifies the authenticity of the sender.

In this guide, I'll go over how to implement a JWT solution within a Spring Boot app. Once you've got it figured out, you can use the same kind of solution for multiple APIs.

TL;DR: There is no TL;DR. This one is going to take some time. You're going to have to go through the steps, make some mistakes, and update your code accordingly.

Of course, you're welcome to just copy the code from GitHub. It should run on Eclipse right out of the box. But if you go that route, you might have trouble understanding what's happening under the covers.

Also, if you'd like a full rundown on JWT before implementing it, check out this guide.

The Use Case

Your boss Smithers walks into your office and lets you know you've done a fine job with the Customer Relationship Management app (CRM) that you're working on.

However, he says as he points his bony finger at you, it needs security. 

Smithers doesn't want just anybody looking at contact info. He needs to know that only authorized sales reps can view contact details.

"And you've got two days to make it happen," he says as he skips out of your office.

It doesn't take long for you to come up with the overall flow, though. You decide that you'll require users to authenticate with a name and password once. In exchange for a valid login, your application will give them a JWT that's valid for 24 hours.

Users will need that JWT to access any resources on the system that reveal contact info.

It Starts With a POM

You're going to need add some dependencies in your POM file for security and JWT. 

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>

 

The first dependency up there is fairly straightforward. That's your starter for using Spring Boot web services. You'll use it to implement controllers that accept REST requests.

Next up is the Spring Boot starter package for security. You'll use that to authenticate the user.

The jjwt dependency is a nifty little package that gets you up and running with JWT in short order.

Finally, the commons-lang3 package gives you some convenience methods that simply makes your life easier.

Configurations

You're going to need to do quite a bit of configurin' before you can implement a JWT solution. Here's the first configuration class:

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException {

		response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
	}
}

 

That class, and its associated interface, are both woefully misnamed. 

Even though the interface is named AuthenticationEntryPoint, it really just handles exceptions thrown during the authentication process.

So, for example, if somebody enters a bad user name and password combination, the method you're looking at above will send them the bad news.

Likewise, if a user tries to access a resource without a JWT, that method will do the Gandalf thing and say "YOU SHALL NOT PASS!"

The next configuration class is significantly more complicated:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

	@Autowired
	private UserDetailsService jwtUserDetailsService;

	@Autowired
	private JwtRequestFilter jwtRequestFilter;

	
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
	}

	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	
	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

	
	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {
		httpSecurity
			.csrf().disable()
			.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
			.authorizeRequests() 
			.antMatchers("/authenticate").permitAll()
			.antMatchers(HttpMethod.GET, "/contact/**").access("hasAuthority('JWT_USER')")
			.anyRequest().authenticated().and()
			.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
			.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
	}
}

 

This is the class that walks hand-in-hand with the security starter we looked at in the POM file.

As you can see it's annotated with both @Configuration and @EnableWebSecurity. The first annotation lets Spring Boot know that it's a class that handles configuration of the overall application. The second lets Spring Boot know that it's going to use web security.

The class also extends WebSecurityConfigurerAdapter. That's because you need to override a couple of the methods in that base class for your own security needs. We'll take a look at that in a moment.

Let's ignore the @Autowired stuff for now because those we'll cover those classes in separate sections.

The first method, configureGlobal(), specifies the UserDetailsService that you'll use to retrieve user info. In a real-world sitch, you'd use that service to grab user info from a back-end database. Here, you'll just mock it up to make things easy.

Please note that the guts of that method also specify a password encoder. That brings us to the next method.

The passwordEncoder() method is annotated with @Bean because you want to make it available to other Spring services. It's using BCryptPasswordEncoder so that we aren't forced to store passwords in clear text.

When somebody logs in with a password, Spring Boot will encrypt that password using BCrypt and check the encrypted string against what's stored in the DB. If it matches, the user is authenticated. It's that simple.

The next method, authenticationManagerBean(), exposes the AuthenticationManager that you built in the configureGlobal() method up above. And yes, even though it's not doing anything but delegating the call to the parent method, it's still required.

Finally, there's the configure() method with HttpSecurity. That one requires its own section.

HttpSecurity

The HttpSecurity object lives up to its name. It handles security for HTTP (or HTTPS) requests.

First up is the csrf().disable() line.  That's going to disable Cross-Site Request Forgery protection. We don't need that protection here because we're using JWT in a stateless environment.

However, you should always conduct your own due diligence to ensure that your security settings meet your requirements. Vulnerabilities abound in this day and age.

Then there's this:

.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)

But what the heck does that do?

Well, recall that there are two ways users can authenticate with the Spring Boot app:

  1. They can provide a user name and password
  2. They can provide a JWT in the request header when trying to access a resource

The line above says, "Let's first check to see if the user has a JWT. If not, then we'll check for a name and password."

The authorizeRequests() line is another one that lives up to its name. It's telling Spring Boot that you want to authorize requests to resources. The next few lines provide specifics on authorization.

This line tells Spring Boot to let all users access the /authenticate resource:

.antMatchers("/authenticate").permitAll()

Why? Because it's ridiculous to expect users to be authenticated when they're trying to authenticate. So you have to let everybody access that resource.

The next line will be of some interest to Smithers:

.antMatchers(HttpMethod.GET, "/contact/**").access("hasAuthority('JWT_USER')")

That tells Spring Boot that people who access the /contact resource (and any resources that start with /contact) with a GET must be authorized to do so. In this case, they need to have the JWT_USER granted authority.

The next line basically says "users need to be authenticated for everything else."

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

Next, take a look at this line:

.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()

If you're paying close attention, you'll see that this line points back to the first configuration class we looked at. Remember? The one I said was woefully misnamed?

This line tells Spring Boot to hit that object in the event of an authentication exception.

And finally:

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

That's telling us to keep the application perfectly stateless. In other words, users need to provide either a JWT or a user name/password combo with every request.

But that's okay because that's what we want here.

The Filter

You're going to need a filter.

What's a filter? In the case of Spring Boot, a filter is an interceptor that handles inspects HTTP requests, does some work with them, and then passes them off to wherever they were going.

Here's the code for the filter:

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

	private static final Logger LOG = LoggerFactory.getLogger(JwtRequestFilter.class);

	
	@Autowired
	private JwtUserDetailsService jwtUserDetailsService;

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

		final String requestTokenHeader = request.getHeader("Authorization");

		String username = null;
		String jwtToken = null;
		
		if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
			jwtToken = requestTokenHeader.substring(7);
			JwtTokenUtil jwtTokenUtil = new JwtTokenUtil(jwtToken);
			
			try {
				username = jwtTokenUtil.getUsernameFromToken();
				validateToken(jwtTokenUtil, username, chain, request, response);
			} catch (IllegalArgumentException e) {
				LOG.error("Unable to get JWT Token", e);
			} catch (ExpiredJwtException e) {
				LOG.error("JWT Token has expired", e);
			}
		} else {
			chain.doFilter(request, response);
		}
	}
	
	
	private void validateToken(JwtTokenUtil jwtTokenUtil, String username, FilterChain chain, HttpServletRequest request, 
			HttpServletResponse response) throws ServletException, IOException {
		
		// Once we get the token validate it.
		if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
			UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(username);

			// if token is valid configure Spring Security to manually set
			// authentication
			if (jwtTokenUtil.validateToken(userDetails)) {
				UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
						userDetails, null, userDetails.getAuthorities());
				
				usernamePasswordAuthenticationToken
						.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
				
				// After setting the Authentication in the context, we specify
				// that the current user is authenticated. So it passes the
				// Spring Security Configurations successfully.
				SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
			}
		}
		
		chain.doFilter(request, response);
	}
}

 

Okay, let's start by taking a look at that first method: doFilterInternal().

As you can see, that method starts by getting the Authorization header. 

Why? Because that's where the JWT will be stored. If there is one.

If there is an Authorization header, the code looks for the word "Bearer" at the beginning of the string.

The reason for that is because it's standard practice to send the JWT as a string after the word "Bearer" in the Authorization header. 

So a JWT in the header gets sent like this:

Bearer eyJhbGciOb2hubnkiL...

I've shortened the token so it all fits on one line. 

If the method finds that the Authorization string starts with "Bearer" then it strips out that word so it only has the token. That's the substring() part.

After that, the code instantiates a JwtUtil object with the JWT. We'll look at the JwtUtil class in more detail later on.

Note that the code also pulls the user name out of the token.  Next, the code invokes the validateToken() method with that user name as one of the parameters.

The validateToken() method gets the full user details from the UserDetailsService object.  Then, it validates the token using the JwtTokenUtil object.

I'll let the comments speak for themselves in that method. To make a long story short, though, that method eventually stores user info in the security context. 

That means any object in the Spring Boot app has access to "the current user." It can grab user info from the security context.

Remember, though, the app only has access to current user info for this request. That's because the application is stateless.

The JwtTokenUtil Class

I mentioned the JwtTokenUtil class in the section above. Let's take a look at it right now.

public class JwtTokenUtil {

	private static final long JWT_TOKEN_VALIDITY = 24 * 60 * 60 * 1000;
	public static final String SECRET = "mysecret";
	
	private String token = null;
	
	public JwtTokenUtil(String jwtToken) {
		this.token = jwtToken;
	}
		
	
	public String getUsernameFromToken() {
		return getClaimFromToken(Claims::getSubject);
	}

	
	public Date getExpirationDateFromToken() {
		return getClaimFromToken(Claims::getExpiration);
	}

	
	public <T> T getClaimFromToken(Function<Claims, T> claimsResolver) {
		final Claims claims = getAllClaimsFromToken();
		return claimsResolver.apply(claims);
	}
	
	
	public String getClaimFromTokenByName(String name) {
		final Claims claims = getAllClaimsFromToken();
		return (String)claims.get(name);
	}
	
	
	private Claims getAllClaimsFromToken() {
		return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
	}

	
	private Boolean isTokenExpired() {
		final Date expiration = getExpirationDateFromToken();
		return expiration.before(new Date());
	}

	
	public static String generateToken(User user) {
		Map<String, Object> claims = addClaims(user); 
		return doGenerateToken(claims, user.getUsername());
	}

	
	private static Map<String, Object> addClaims(User user) {
		Map<String, Object> claims = new HashMap<String, Object>();
		
		claims.put("id", user.getId());
		
		return claims;
	}	

	
	private static String doGenerateToken(Map<String, Object> claims, String subject) {
		return Jwts.builder()
				.setClaims(claims)
				.setSubject(subject)
				.setIssuedAt(new Date(System.currentTimeMillis()))
				.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY))
				.signWith(SignatureAlgorithm.HS512, SECRET).compact();
	}

	
	public Boolean validateToken(UserDetails userDetails) {
		final String username = getUsernameFromToken();
		return (username.equals(userDetails.getUsername()) && !isTokenExpired());
	}
	
	public Boolean validateToken() {
		return (!isTokenExpired());
	}
}

 

I won't cover every method in that class, as some of them are fairly intuitive. Let's just look at a couple of them.

For starters, you should know that this is the class that works with the jjwt dependency your incorporated into the application way back in the first step.

Next, take a look at the JWT_TOKEN_VALIDITY constant declared at the top of the class. That's a long that sets the expiration time of the token. 

It's a long because it specifies the expiration in milliseconds from now. If you do the math, you'll find the product equals 24 hours or one day.

The SECRET constant is the string you use to sign the JWT. It's called SECRET for a good reason. You shouldn't share it with unauthorized individuals.

Next, note that the JwtTokenUtil class includes only a single constructor. That constructor takes a String parameter that's the token itself.

The first method in the class, getUsernameFromToken(), is fairly self-explanatory. It grabs the "sub" (or Subject) registered claim from the payload and returns it.

It's worth noting that in this app the user name and the subject are identical. So you don't need to specifically set the user name in the token.

Also remember: the user name or subject is particularly important here because that's what UserDetailsService uses to look up the user. So your application better make darn sure that user names are unique!

The addClaims() method does exactly what you'd expect. It creates a set of claims that will get put in the token.

In this case, you're just putting the ID attribute of the user in the token. 

The doGenerateToken() method does the fun stuff associated with creating the JWT. As you can see, it sets the claims, the subject, the date of issuance, and the expiration date. Then, it signs that package using the HS512 algorithm and the secret key defined above.

UserDetailsService

This is the service used to look up a user by user name.

@Service
public class JwtUserDetailsService implements UserDetailsService {

    @Autowired
    private PasswordEncoder encoder;

	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User user = findByUsername(username);
		return user;
	}
	
	
	private User findByUsername(String username) {
	    User user = null;
	    
	    if ("johnny".equals(username)) {
	        user = getJohnny();
	    }
	    
	    return user;
	}
	
	
	private User getJohnny() {
	    User user = new User();
	    
	    user.setFirstName("Johnny");
	    user.setLastName("Smith");
	    user.setId("1");
	    user.setUsername("johnny");
	    user.setPassword(encoder.encode("kleptocracy"));
	    
	    List<String> authorityNames = new ArrayList<String>();
	    authorityNames.add("JWT_USER");
	    user.setAuthorityNames(authorityNames);
	    
	    return user;
	}
}

 

Once again, you'd normally use this kind of service to grab the user info from a data store. Here, we're just keeping things simple with a single, hard-coded user.

That user is defined in getJohnny(). There, the code sets a few pieces of info about this mythical Johnny character, including his first name, last name, user name, and ID.

Note that the password is encoded. Remember: we need to do that because Spring Boot will encrypt the clear text password provided by the user and try to match the encrypted text with whatever is stored in the DB.

Also, we're adding an authority to Johnny. In this case, the authority is named JWT_USER

You can think of an authority as a group of users allowed to perform specific tasks. If you go back to the WebSecurityConfig class, you'll see where the application uses JWT_USER .

That authority gives Johnny the rights to access any resource that begins with /contact in the URL path. 

The Request & Response Models

Let's take a quick peek at the request and response models before we test this thing out.

Here's the request for a JWT:

public class JwtRequest {
	
	private String username;
	private String password;	
	
	public JwtRequest() { }

	public JwtRequest(String username, String password) {
		this.setUsername(username);
		this.setPassword(password);
	}

	public String getUsername() {
		return this.username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return this.password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

 

Nothing but a user name and password. Nice and neat.

Remember, though, HTTP requests for REST services don't come in as Java objects. They're sent predominantly as JSON objects (although sometimes XML).

So the JSON object that the application wants to see looks something like this:

{"username" : "johnny", "password" : "kleptocracy"}

Well, actually, it looks exactly like that since that's Johnny's user name and password.

Next, let's look at the response:

public class JwtResponse {

	private final String token;
	private final User user;

	public JwtResponse(String token, User user) {
		this.token = token;
		this.user = user;
	}

	public String getToken() {
		return this.token;
	}

	public User getUser() {
		return user;
	}	
}

 

That's fairly simple also. Just the token and the user get returned.

The response will look like this in JSON format:

{

    "token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJqb2hubnkiLCJpZCI6IjEiLCJleHAiOjE1OTgyNzI5MTgsImlhdCI6MTU5ODE4NjUxOH0.KIH2BiGpKtRQNTQuwyN8m0ySAqpdl2q6z6auClvKsUtQaufn6QD3uI0Wm0yB7NiBSvSgIqxBo5i1c-2JsIxFxA",

    "user": {

        "id": "1",

        "firstName": "Johnny",

        "lastName": "Smith",

        "authorityNames": [

            "JWT_USER"

        ],

        "username": "johnny",

        "enabled": true,

        "authorities": [

            {

                "authority": "JWT_USER"

            }

        ],

        "credentialsNonExpired": true,

        "accountNonExpired": true,

        "accountNonLocked": true

    }

}

If you look at the getJohnny() method from the previous section, you'll see that many of the details match up perfectly.

But what about those items at the bottm? Where do credentialsNonExpired, accountNonExpired, and accountNonLocked come from?

Glad you asked.

The User Object

Let's take a quick look at the User object.

public class User implements UserDetails {

	private static final long serialVersionUID = 3592549577903104696L;

	private String id;
	private String firstName;
	private String lastName;
	private String street1;
	private String street2;
	private String city;
	private String state;
	private String zip;
	private String email;
	private String phoneNumber;
	private List<String> authorityNames = new ArrayList<String>();
	private String username;
	private String country;
	private String profileImageFileName;
...
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}
	
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}
	
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}
	
	@Override
	public boolean isEnabled() {
		return true;
	}
}

 

We don't need to cover all the getters and setters here. But pay attention to those last three methods. What are they all about?

Take a look at the class declaration. You'll see that User implements UserDetails. That interface requires those three methods.

And that's why you see those fields in the response.

To keep things simple here, you're just setting all those booleans to true, meaning that the user is always valid. In reality, you'd perform some logic to check and make sure that the user isn't disabled, or the account isn't expired, etc.

Why do you need to implement UserDetails? Because it's required by Spring Security to retrieve user info.

Alternatively, you could extend the Spring User class. 

The Controller

This application includes a couple of controllers. To streamline this tutorial, I'm just going to cover the one that handles authentication requests.

Here's what that controller looks like:

@RestController
public class JwtAuthenticationController {

	@Autowired
	private AuthenticationManager authenticationManager;

	
	@PostMapping(value = "/authenticate")
	public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest) throws Exception {
		final User user = authenticate(authenticationRequest);
		final String token = JwtTokenUtil.generateToken(user);		
		
		return ResponseEntity.ok(new JwtResponse(token, user));
	}

	
	private User authenticate(JwtRequest request) throws Exception {
		User user = null;
		
		try {
			Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
			user = (User)auth.getPrincipal();
		} catch (DisabledException e) {
			throw new Exception("USER_DISABLED", e);
		} catch (BadCredentialsException e) {
			throw new Exception("INVALID_CREDENTIALS", e);
		}
		
		return user;
	}
}

 

As you can see, that class includes only one method that handles an API endpoint. It's the createAuthenticationToken() method and it's mapped to /authenticate.

Please note also that the /authenticate endpoint uses a POST request. So testing this out will require more than just typing a URL into a browser bar.

Now, take a look at the single parameter in the method signature. The @RequestBody annotation tells Spring Boot to grab the contents in the request body and translate those contents into an object. In this case, it will translate the body to a JwtRequest object that we looked at a couple of sections back.

So, once again, the JSON request that the app is looking for should follow this pattern:

{"username" : "johnny", "password" : "kleptocracy"}

Spring Boot will use that name and password to authenticate the user. Then, the application will send back a JwtResponse object that includes the token.

As we've seen, the token will be valid for 24 hours and the user will be required to use that token for subsequent requests.

Testing It Out

Okay, we've looked at the code. Now let's see if this thing actually works as expected.

A caveat, though: we haven't looked at all of the code. Some subjects I'll cover in future guides (no promises).

Also, look here.if you'd like an explainer on the code that retrieves a contact by ID.

Start by deploying the app. From within Eclipse, just right-click on the JwtGuide class. Then select Run As... and Java Application.

Once the application is up and running, let's get that token.

To do that, we'll need to send a POST request. My favorite tool for that kind of task is the appropriately named Postman. But you can also use Fiddler or a Google Chrome plugin.

You already know from reviewing the code above that there's only one user defined in the system. That user's name is "johnny" and the password is "kleptocracy".

So that's the user name and password you'll need to send to get the credentials.

Let's assume that you went with all the defaults in your Spring Boot deployment. Your application is deployed to localhost and listens on port 8080.

And, remember, the path for getting the JWT is /authenticate. It's expecting a POST request.

Add all that up and you know you're going to have to send a POST request with a user name of "johnny" and a password of "kleptocracy" to this endpoint:

http://localhost:8080/authenticate

So fire up Postman and make that happen!

Once Postman is up and running, click that plus sign in the open tab to start a new request.

 

Enter the URL in the space with the "Enter request URL" placeholder text. You can literally just copy and paste it from the URL above if you're using the default settings for Spring Boot on a local deployment.

Also, make sure you change the request type from GET to POST.

 

But you ain't done yet. You need a request body.

Click on that Body tab you see just below the URL and between Headers and Pre-request Script. Even though it doesn't look like a tab, it is.

Once you're in the Body tab, click on the raw radio button.

And once again, you can just copy and paste the JSON request from above into the body section. Also, select JSON from the drop-down on the right.

It will look like this:

 

At this point, you're all set. Click the big blue Send button towards the upper, right-hand corner. You should see something like this in the response section:

{

    "token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJqb2hubnkiLCJpZCI6IjEiLCJleHAiOjE1OTgyOTcwMTUsImlhdCI6MTU5ODIxMDYxNX0.TaEzwxtXwevEqpEKOJC_kVlr5EAAigxTkROOLaej3ViB57Pv6keK7KupYuuueGYK2BNBlX27i4P6I-ir8y7NlQ",

    "user": {

        "id": "1",

        "firstName": "Johnny",

        "lastName": "Smith",

        "street1": null,

        "street2": null,

        "city": null,

        "state": null,

        "zip": null,

        "email": null,

        "phoneNumber": null,

        "authorityNames": [

            "JWT_USER"

        ],

        "username": "johnny",

        "country": null,

        "profileImageFileName": null,

        "enabled": true,

        "authorities": [

            {

                "authority": "JWT_USER"

            }

        ],

        "accountNonExpired": true,

        "accountNonLocked": true,

        "credentialsNonExpired": true

    }

}

Well that looks familiar, doesn't it? You already saw that in one of the previous sections.

But this time you need to take action. You see that token at the top? Copy and paste it to a Notepad document or some other scratchpad. 

Now, make a request to a resource. Get the details of the contact with the user ID of 1.

Here's what that request will look like:

http://localhost:8080/contact/1

Contrary to the other request, this is a GET request. However, you need to set a header.

So begin by creating a new request tab in Postman. Once again, click that plus button at the top.

Enter the URL above. Leave the GET request type alone this time.

Now, click on the Headers tab below the URL. You need to enter an Authorization header that includes the token you just received.

Start by clicking on the top header field. It will have a check mark next to it.

Start typing "Authorization." It will probably auto-complete so you don't need to type the whole word.

 

Now that you've specified the header, you need to specify the value for that header. 

You may think you can just copy and paste the token into the value field. But you can't.

As I mentioned above, the value begins with the word "Bearer". You need to type that word followed by a space and then the token.

The whole thing will look something like this:

Bearer eyJhbGciOiJIUzUx...

There's a carriage return instead of a space above because the whole token won't fit on one line. That's okay, though, it's whitespace.

So here's what you'll see in Postman:

 

And now you're once again ready to click the big blue Send button. Try it out.

You should see this in the response window:

{

    "id": 1,

    "firstName": "James",

    "lastName": "Buffet",

    "authority": true,

    "email": "jimmy.buffet@xmail.com",

    "salesOwnerId": 1

}

 

And that's correct because Jimmy Buffet does indeed have an ID of 1.

Negative Testing

Since we're testing security, it's a really good idea to do some negative testing as well. Let's make sure that unauthorized critters can't get into the system.

Start by sending an /authenticate request with a bad user name or password.  Something like this should do the trick:

{"username" : "johnny", "password" : "xxx"}

Just go back to the first tab in Postman and send that off. You should see this:

{

    "timestamp": "2020-08-23T19:44:13.080+0000",

    "status": 401,

    "error": "Unauthorized",

    "message": "Bad credentials",

    "path": "/authenticate"

}

 

And that's what you want to see because the password doesn't match what's in the sytem.

Next, try to send a request without the JWT. Just uncheck that Authorization header in the second tab.

Send that away and you should see this:

{

    "timestamp": "2020-08-23T19:45:47.341+0000",

    "status": 401,

    "error": "Unauthorized",

    "message": "Full authentication is required to access this resource",

    "path": "/contact/1"

}

Sucess. You win again.

Wrapping It Up

So there you have it: a starting point for implementing a JWT solution within your Spring Boot application.

Homework assignment: go through the code on Github. Look for ways to refactor and improve what's already there.

Also: don't rely solely on this article for your security needs. By the time you're reading this, new threats could have popped up. Do your own due diligence to harden your sytem and make sure your data is secure.

Finally, add some additional security measures. Create new authorities and only allow users with those authorities to access different enpoints.

As always, have fun!