Spring Security with JWT authentication.

disha saxena
6 min readDec 2, 2020

Spring Security is a framework that provides various security features like authentication, authorization to create secure Java Enterprise Applications.

It overcomes all the problems that come during creating non-spring security applications and manage a new server environment for the application.

This framework targets two major areas of application are authentication and authorization. Authentication is the process of knowing and identifying the user that wants to access.

Spring security is an application framework that is used for application-level security such as

  1. Login and Logout functionality.
  2. Allow/Block access to URLs to the logged-in users.
  3. Alow/Block access to URLs to logged-in users with certain roles.

→Authorization is the process to allow the authority to perform actions in the application. Authorization in system security is the process of giving the user permission to access a specific resource or function. This term is often used interchangeably with access control or client privilege. (Can this user do this? Yes or No)

NOTE: Only after authentication, you can do authorization

→Authentication is the act of validating that users are who they claim to be. (basically is asks WHO ARE YOU?)

🔸 Passwords. Usernames and passwords are the most common authentication factors. If a user enters the correct data, the system assumes the identity is valid and grants access.

🔸One-time pins. Grant access for only one session or transaction.

🔸Authentication apps. Generate security codes via an outside party that grants access.

🔸Biometrics. A user presents a fingerprint or eye scan to gain access to the system.

Advantages of using spring security:

  • Comprehensive support for authentication and authorization.
  • Protection against common tasks
  • Servlet API integration
  • Integration with Spring MVC
  • Portability
  • CSRF protection
  • Java Configuration support

HTTP is a stateless protocol. What it means is every interaction in HTTP needs to contain all the information needed for that interaction. Nothing is remembered from before. No state is maintained over multiple requests.

The problem is when the response from the server is dynamic and it depends on who the user is. In this case, the information you send to the server is not just what page you want, you obviously also need to tell the server who you are.

There are multiple ways that web applications manage and remember sessions. The two popular options are:

1. Using session tokens

2. Using JSON Web tokens or JWT.

A JSON Web Token is used to send information that can be verified and trusted by means of a digital signature. It comprises a compact and URL-safe JSON object, which is cryptographically signed to verify its authenticity, and which can also be encrypted if the payload contains sensitive information.

What is the JSON Web Token structure?

In its compact form, JSON Web Tokens consist of three parts separated by dots (.), which are:

  • Header
  • Payload
  • Signature

Therefore, a JWT typically looks like the following.

xxxxx.yyyyy.zzzzz

header:

The header typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.

Payload:

The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims: registered, public, and private claims.

Signature:

To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.

SecurityConfigurer.java

@Configuration
@EnableWebSecurity
class SecurityConfigurer extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService myUserDetailsService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

@Autowired
private JwtRequestFilter jwtRequestFilter;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors();

httpSecurity.csrf().disable()
.authorizeRequests().antMatchers(“/api/authenticate”,”/api/register”).permitAll().
anyRequest().authenticated().and().
exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}}

WebAppConfig.java

@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController(“/”).setViewName(“forward:/index.html”);
}

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping(“/**”).allowedOrigins(“http://localhost:3000").
allowCredentials(true).allowedMethods(“*”);
}

@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}

JwtUtil.java

@Servicepublic class JwtUtil {@Value(“${jwt.secret}”)private String SECRET_KEY ;// private String SECRET_KEY = “secret”;public String extractUsername(String token) {return extractClaim(token, Claims::getSubject);}public Date extractExpiration(String token) {return extractClaim(token, Claims::getExpiration);}public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {final Claims claims = extractAllClaims(token);return claimsResolver.apply(claims);}private Claims extractAllClaims(String token) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();}private Boolean isTokenExpired(String token) {return extractExpiration(token).before(new Date());}public String generateToken(UserDetails userDetails) {Map<String, Object> claims = new HashMap<>();return createToken(claims, userDetails.getUsername());}private String createToken(Map<String, Object> claims, String subject) {return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())).setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)).signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();}public Boolean validateToken(String token, UserDetails userDetails) {final String username = extractUsername(token);return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));}}

MyUserDetailsService.java

@Servicepublic class MyUserDetailsService implements UserDetailsService {@AutowiredEmployeeDAO employeedao;public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {EmployeeEntity employee=employeedao.findByEmailAndPasswordNotNull(email);User user=new User(employee.getEmail(),employee.getPassword(),new ArrayList<>());return user;}}

AuthenticationRequest.java

public class AuthenticationRequest implements Serializable {private String email;private String password;public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}//need default constructor for JSON Parsingpublic AuthenticationRequest(){}public AuthenticationRequest(String username, String password) {this.setEmail(email);this.setPassword(password);}}

AuthenticationResponse.java

public class AuthenticationResponse implements Serializable {private final String jwt;private final String name;public AuthenticationResponse(String jwt,String name) {this.jwt = jwt;this.name=name;}public String getJwt() {return jwt;}public String getName() {return name;}}

JwtRequestFilter.java

@Componentpublic class JwtRequestFilter extends OncePerRequestFilter {@Autowiredprivate MyUserDetailsService userDetailsService;@Autowiredprivate JwtUtil jwtUtil;@AutowiredUserEmailDTO userEmailDTO;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {final String authorizationHeader = request.getHeader(“Authorization”);String username = null;String jwt = null;if (authorizationHeader != null && authorizationHeader.startsWith(“Bearer “)) {jwt = authorizationHeader.substring(7);username = jwtUtil.extractUsername(jwt);}if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);if (jwtUtil.validateToken(jwt, userDetails)) {userEmailDTO.setEmail(userDetails.getUsername());UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);}}chain.doFilter(request, response);}}

SecurityController.java

@RestControllerpublic class SecurityController {@AutowiredEmployeeService employeeService;@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate JwtUtil jwtTokenUtil;@Autowiredprivate MyUserDetailsService userDetailsService;@RequestMapping({ “/hello” })public String firstPage() {return “Hello World”;}@RequestMapping(value = “/api/authenticate”, method = RequestMethod.POST)public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {try {authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getEmail(), authenticationRequest.getPassword()));}catch (BadCredentialsException e) {throw new Exception(“Incorrect username or password”, e);}final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getEmail());String name=employeeService.getEmployeeNameByEmail(userDetails.getUsername());final String jwt = jwtTokenUtil.generateToken(userDetails);return ResponseEntity.ok(new AuthenticationResponse(jwt,name));}}

WebSecurityConfigurerAdapter is the crux of our security implementation. It provides HttpSecurity configurations to configure cors, csrf, session management, rules for protected resources. We can also extend and customize the default configuration that contains the elements below.

UserDetailsService interface has a method to load User by username and returns a UserDetails object that Spring Security can use for authentication and validation.

UserDetails contains necessary information (such as: username, password, authorities) to build an Authentication object.

UsernamePasswordAuthenticationToken gets {username, password} from login Request, AuthenticationManager will use it to authenticate a login account.

AuthenticationManager has a DaoAuthenticationProvider (with help of UserDetailsService & PasswordEncoder) to validate UsernamePasswordAuthenticationToken object. If successful, AuthenticationManager returns a fully populated Authentication object (including granted authorities).

OncePerRequestFilter makes a single execution for each request to our API. It provides a doFilterInternal() method that we will implement parsing & validating JWT, loading User details (using UserDetailsService), checking Authorizaion (using UsernamePasswordAuthenticationToken).

AuthenticationEntryPoint will catch authentication error.

Repository contains UserRepository & RoleRepository to work with Database, will be imported into Controller.

Controller receives and handles request after it was filtered by OncePerRequestFilter.

……………………………… by Disha Saxena and Kshitiz Mohan

--

--