top of page

Spring Security - JWT Authentication - Series 1

  • Writer: Anand Nerurkar
    Anand Nerurkar
  • Apr 4, 2022
  • 5 min read

Updated: Apr 10, 2022

Let us create a REST API & access endpoint with JWT token, please follow below steps for your reference. Here we will make use of plain text based password for simplicity and making use of hardcoded username and password for keeping it simple.


Prerequisite Knowledge

Spring Boot

Java 8

Maven

Spring Security

Spring Starter https://start.spring.io/

Eclipse IDE



1. Use spring starter to create spring boot project and mention the required dependency.

ree

2. Click on generate.

project will be generated and downloaded to your local disk in the form of zip

unzip the project & import project into eclipse


3. Select project and run maven install

It should build successfully and can see default password generated since we have

included spring security in class path.


-------------server console log sample start ---------------

2022-04-04 10:31:59.160 WARN 4332 --- [ main] .s.s.UserDetailsServiceAutoConfiguration :

Using generated security password: bfd8810c-2439-4f0d-8fe1-92eadf4a3794

This generated password is for development use only. Your security configuration must be updated before running your application in production.

2022-04-04 10:31:59.376 INFO 4332 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will not secure any request

-----------server console log sample end-----------


4. Let us create model,controller,service & config package


ree

5. Let us create UserService that make use of spring UserDetailsService for authentication

purpose. Currently we will hardcode user for simplicity. Please see below code sample

--------------UserService Sample--------

/**

*

*/

package com.techbytes.jwtdemo.service;

import java.util.ArrayList;

import org.springframework.security.core.userdetails.User;

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;

/**

* @author andyn

*

*/

@Service

public class UserService implements UserDetailsService {


/**

*

*/

public UserService() {

// TODO Auto-generated constructor stub

}


@Override

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

// TODO Auto-generated method stub

/*

* logic to get the user from database

* User user=userRepository.findByUsername(username);

* return spring user as below with username,password and granted authority

* return new User(user.getUsername(),user.getPassword(),new ArrayList<T>());

*/

/*

* currently we are hardcoding the user as below

*/

return new User("admin","password",new ArrayList());

}


}



6. Let us create securityconfiguration

  • Securityconfiguration extend webSecurityConfigurerAdapter

  • Autowire userservice for authentication.

  • Override configure(AuthenticationManagerBuilder )

  • Annotate class with @EnableWebSecurity,@configuration

As we are using simple text based password and by default spring does encrypt password, we need to instruct spring for plain password, so we make use of noopspasswordencoder bean in main class as below


------------JWTDemoApplication-Main class--------

====

package com.techbytes.jwtdemo;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.Bean;

import org.springframework.security.crypto.password.NoOpPasswordEncoder;

import org.springframework.security.crypto.password.PasswordEncoder;


@SpringBootApplication

public class JwtDemoApplication {


public static void main(String[] args) {

SpringApplication.run(JwtDemoApplication.class, args);

}


@Bean

public PasswordEncoder passwordEncoder() {

return NoOpPasswordEncoder.getInstance();

}

}


---------------SecurityConfiguration-------------------

/**

*

*/

package com.techbytes.jwtdemo.config;


import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


import com.techbytes.jwtdemo.service.UserService;


/**

* @author andyn

*

*/

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


/**

*

*/

@Autowired

UserService userService;

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {


auth.userDetailsService(userService);

}


}


7. Let us create sample controller


/**

*

*/

package com.techbytes.jwtdemo.controller;

import org.springframework.web.bind.annotation.GetMapping;

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

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


/**

* @author andyn

*

*/

@RestController

@RequestMapping("/api")

public class SampleConroller {

@GetMapping("/sample")

public String welcome() {

return "Welcome To JWT Authentication-Text Based Password";

}


}


add below entry in resources/application.yml

server:

port: 9092

servlet:

context-path: /api


7. Build & Run

Select your project, run maven install, this will build your project successfully.

Select JwtDemoApplication & run Java application


Since security is enabled ,spring security open http basic form and will redirect you to

ree

please enter hardcoded username as admin , password as password and click submit.


Your endpoint will /sample will return response as below.


ree


Till this step we have seen how spring security with basic form work.


8. Now we will proceed further for JWT authentication.

Please include below dependency in pom.xml file


<dependency>

<groupId>javax.xml.bind</groupId>

<artifactId>jaxb-api</artifactId>

</dependency>

<dependency>

<groupId>io.jsonwebtoken</groupId>

<artifactId>jjwt</artifactId>

<version>0.9.1</version>

</dependency>


9. Create JWTUtility class that will take care of generating token,validating token.

-----------------JWTUtility-----------------------

package com.techbytes.jwtdemo.util;

import io.jsonwebtoken.Claims;

import io.jsonwebtoken.Jwts;

import io.jsonwebtoken.SignatureAlgorithm;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.security.core.userdetails.UserDetails;

import org.springframework.stereotype.Component;

import java.io.Serializable;

import java.util.Date;

import java.util.HashMap;

import java.util.Map;

import java.util.function.Function;


@Component

public class JWTUtility implements Serializable {


private static final long serialVersionUID = 234234523523L;


public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;


@Value("${jwt.secret}")

private String secretKey;


//get username from jwt token

public String getUsernameFromToken(String token) {

return getClaimFromToken(token, Claims::getSubject);

}


//get expiration date from jwt token

public Date getExpirationDateFromToken(String token) {

return getClaimFromToken(token, Claims::getExpiration);

}


public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {

final Claims claims = getAllClaimsFromToken(token);

return claimsResolver.apply(claims);

}


//for fetching any information from token we will need the secret key

private Claims getAllClaimsFromToken(String token) {

return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();

}


//check if the token has expired

private Boolean isTokenExpired(String token) {

final Date expiration = getExpirationDateFromToken(token);

return expiration.before(new Date());

}



//generate token for user

public String generateToken(UserDetails userDetails) {

Map<String, Object> claims = new HashMap<>();

return doGenerateToken(claims, userDetails.getUsername());

}


/*

* while creating the token -

* Define claims of the token - Issuer, Expiration, Subject, and the ID

Sign the JWT using the HS512 algorithm and secret key

*/


private 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 * 1000))

.signWith(SignatureAlgorithm.HS512, secretKey).compact();

}



//validate token

public Boolean validateToken(String token, UserDetails userDetails) {

final String username = getUsernameFromToken(token);

return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));

}

}


10. Let us create one endpoint /authenticate with jwtreq, jwtresp object in sample controller


@PostMapping("/authenticate")

public JwtResp authenticate(@RequestBody JwtReq jwtReq) throws Exception{


try {

authenticationManager.authenticate(

new UsernamePasswordAuthenticationToken(

jwtReq.getUserName(),

jwtReq.getPassword()

)

);

} catch (BadCredentialsException e) {

throw new Exception("INVALID_CREDENTIALS", e);

}

// fetch user from userservice to get userdetails

final UserDetails userDetails

= userService.loadUserByUsername(jwtReq.getUserName());


// generate jwttoken with username

final String token =

jwtUtility.generateToken(userDetails);


return new JwtResp(token);

}


-------------------------JWTReq----------------


/**

*

*/

package com.techbytes.jwtdemo.model;


import java.io.Serializable;


/**

* @author andyn

*

*/

public class JwtReq implements Serializable {


String userName;

String password;

/**

* @return the userName

*/

public String getUserName() {

return userName;

}

/**

* @param userName the userName to set

*/

public void setUserName(String userName) {

this.userName = userName;

}

/**

* @return the password

*/

public String getPassword() {

return password;

}

/**

* @param password the password to set

*/

public void setPassword(String password) {

this.password = password;

}

public JwtReq(String userName, String password) {

super();

this.userName = userName;

this.password = password;

}

public JwtReq() {

super();

}

}


---------------------------JWTResp-----------------

/**

*

*/

package com.techbytes.jwtdemo.model;


import java.io.Serializable;


/**

* @author andyn

*

*/

public class JwtResp implements Serializable {

String jwtToken;


public JwtResp() {

super();

// TODO Auto-generated constructor stub

}


public JwtResp(String jwtToken) {

super();

this.jwtToken = jwtToken;

}


/**

* @return the jwtToken

*/

public String getJwtToken() {

return jwtToken;

}


/**

* @param jwtToken the jwtToken to set

*/

public void setJwtToken(String jwtToken) {

this.jwtToken = jwtToken;

}


}


11. update security configuration class with below bean


@Bean

public AuthenticationManager authenticationManagerBean() throws Exception {

return super.authenticationManagerBean();

}




12. Add below property in resources/application.yml


jwt

secret: secretkey123



we need to make sure that /authenticate should be permitted and rest endpoint should be authenticated. So please include below method in securityconfiguration class.


@Override

protected void configure(HttpSecurity http) throws Exception {

http.csrf()

.disable()

.authorizeRequests()

.antMatchers("/authenticate")

.permitAll()

.anyRequest()

.authenticated()

.and()

.sessionManagement()

.sessionCreationPolicy(SessionCreationPolicy.STATELESS);

// http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);


}



13. Build the project and run application.

Let us open the request in postman and will hit /api/sample endpoint.

You should get 403- forbidden


ree


Now we will generate token with /authenticate endpoint in postman with required details as shown below to generate token.



ree


Now hit /api/sample without token, will receive 403 status code


ree

Now hit /api/sample with token, as mentioned in below screenshot. You will receive response.



ree





 
 
 

Recent Posts

See All

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
  • Facebook
  • Twitter
  • LinkedIn

©2024 by AeeroTech. Proudly created with Wix.com

bottom of page