Spring Security with JWT authentication.
By: Disha Saxena
Tags: Security, Spring Boot, Jwt Token, Json Web Token, Spring Security

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

Login and Logout functionality. Allow/Block access to URLs to the logged-in users. Alow/lock 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 {

public void addViewControllers(ViewControllerRegistry registry) {

registry.addViewController(“/”).setViewName(“forward:/index.html”);

}



@Override

public void addCorsMappings(CorsRegistry registry) {

registry.addMapping(“/**”).allowedOrigins(“

allowCredentials(true).allowedMethods(“*”);

}



@Bean

public ModelMapper modelMapper() {

return new ModelMapper();

} @Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController(“/”).setViewName(“forward:/index.html”);@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping(“/**”).allowedOrigins(“ http://localhost:3000 ").allowCredentials(true).allowedMethods(“*”);@Beanpublic ModelMapper modelMapper() {return new ModelMapper(); }

JwtUtil.java

@Service public 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

@Service public class MyUserDetailsService implements UserDetailsService { @Autowired EmployeeDAO 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 Parsing public 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

@Component public class JwtRequestFilter extends OncePerRequestFilter { @Autowired private MyUserDetailsService userDetailsService; @Autowired private JwtUtil jwtUtil; @Autowired UserEmailDTO userEmailDTO; @Override protected 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

@RestController public class SecurityController { @Autowired EmployeeService employeeService; @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtUtil jwtTokenUtil; @Autowired private 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