Following are the required Steps to configure, RestAPI token authentication in spring boot using JWT and Spring boot using JPA, MYSQL, and PostMan.
Maven Dependencies
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency>
The main class of Spring boot
ProjectName_Application.Java:
package com.jwt; import com.jwt.model.User; import com.jwt.repo.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.Random; @SpringBootApplication public class JwtauthenticationserverApplication implements CommandLineRunner { @Autowired private UserRepository userRepository; Random random = new Random(); public void createUsers() { User user = new User(); Long id = new Long(random.nextInt(100)); user.setId(id); user.setEmail("user" + id + "@gmail.com"); user.setPassword("user" + id); user.setEnabled(true); user.setRol("Admin"); user.setUsername("user" + id); User save = this.userRepository.save(user); System.out.println(save); } public static void main(String[] args) { SpringApplication.run(JwtauthenticationserverApplication.class, args); } @Override public void run(String... args) throws Exception { createUsers(); } }
Configuration Package(com.jwt.config)
JwtAuthenticationEntryPoint.java
package com.jwt.config; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.sendError(401, "Unauthorized"); } }
JwtAuthenticationFilter.java
package com.jwt.config; import com.jwt.helper.JwtUtil; import com.jwt.services.CustomUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private CustomUserDetailsService customUserDetailsService; @Autowired private JwtUtil jwtUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //get jwt //Bearer //validate String requestTokenHeader = request.getHeader("Authorization"); String username=null; String jwtToken=null; //null and format if(requestTokenHeader!=null && requestTokenHeader.startsWith("Bearer ")) { jwtToken=requestTokenHeader.substring(7); try{ username = this.jwtUtil.getUsernameFromToken(jwtToken); }catch (Exception e) { e.printStackTrace(); } if(username!=null && SecurityContextHolder.getContext().getAuthentication()==null) { UserDetails userDetails = this.customUserDetailsService.loadUserByUsername(username); //security UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); }else { System.out.println("Token is not validated.."); } } filterChain.doFilter(request,response); } }
MySecurityConfig.java
package com.jwt.config; import com.jwt.services.CustomUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 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; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableWebSecurity public class MySecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserDetailsService customUserDetailsService; @Autowired private JwtAuthenticationFilter jwtFilter; @Autowired private JwtAuthenticationEntryPoint entryPoint; @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() .disable() .cors() .disable() .authorizeRequests() .antMatchers("/token").permitAll() .antMatchers(HttpMethod.OPTIONS).permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .exceptionHandling().authenticationEntryPoint(entryPoint); http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); } //over @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserDetailsService); } @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
com.jwt.controller Package
JwtController.java
package com.jwt.controller; import com.jwt.helper.JwtUtil; import com.jwt.model.JwtRequest; import com.jwt.model.JwtResponse; import com.jwt.services.CustomUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.web.bind.annotation.*; @RestController @CrossOrigin(origins = "*") public class JwtController { @Autowired private AuthenticationManager authenticationManager; @Autowired private CustomUserDetailsService customUserDetailsService; @Autowired private JwtUtil jwtUtil; @RequestMapping(value = "/token", method = RequestMethod.POST) public ResponseEntity<?> generateToken(@RequestBody JwtRequest jwtRequest) throws Exception { System.out.println("Inside Controller"); System.out.println(jwtRequest); try { this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(jwtRequest.getUsername(), jwtRequest.getPassword())); } catch (UsernameNotFoundException e) { e.printStackTrace(); throw new Exception("Bad Credentials"); }catch (BadCredentialsException e) { e.printStackTrace(); throw new Exception("Bad Credentials"); } //fine area.. UserDetails userDetails = this.customUserDetailsService.loadUserByUsername(jwtRequest.getUsername()); String token = this.jwtUtil.generateToken(userDetails); System.out.println("JWT " + token); //{"token":"value"} return ResponseEntity.ok(new JwtResponse(token)); } }
com.jwt.util
JwtUtil.java
package com.jwt.helper; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.cglib.core.internal.Function; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import java.util.Date; import java.util.HashMap; import java.util.Map; @Component public class JwtUtil { private static final long serialVersionUID = -2550185165626007488L; public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60; private String secret="java"; //retrieve username from jwt token public String getUsernameFromToken(String token) { return getClaimFromToken(token, Claims::getSubject); } //retrieve 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 retrieveing any information from token we will need the secret key private Claims getAllClaimsFromToken(String token) { return Jwts.parser().setSigningKey(secret).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 - //1. Define claims of the token, like Issuer, Expiration, Subject, and the ID //2. Sign the JWT using the HS512 algorithm and secret key. //3. According to JWS Compact Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1) // compaction of the JWT to a URL-safe string 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, secret).compact(); } //validate token public Boolean validateToken(String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } }
com.jwt.model
CustomUserDetails.java
package com.jwt.model; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.List; public class CustomUserDetails implements UserDetails { private User user; public CustomUserDetails(User user) { this.user = user; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(user.getRol()); return List.of(simpleGrantedAuthority); } @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getUsername(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
JwtRequest.java
package com.jwt.model; public class JwtRequest { String username; String password; public JwtRequest() { } public JwtRequest(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "JwtRequest{" + "username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
JwtResponse.java
package com.jwt.model; public class JwtResponse { String token; public JwtResponse() { } public JwtResponse(String token) { this.token = token; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } }
User.java
package com.jwt.model; import javax.persistence.*; @Entity @Table(name = "USER") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String username; private String password; private String email; private String rol; private boolean enabled; //more properties as your project requirements public User() { } public User(Long id, String username, String password, String email, String rol, boolean enabled) { this.id = id; this.username = username; this.password = password; this.email = email; this.rol = rol; this.enabled = enabled; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getRol() { return rol; } public void setRol(String rol) { this.rol = rol; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", email='" + email + '\'' + ", rol='" + rol + '\'' + ", enabled=" + enabled + '}'; } }
com.jwt.repo
UserRepository.java(Interface)
package com.jwt.repo; import com.jwt.model.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> { //username , it will return the user of given username public User findByUsername(String username); }
com.jwt.services
CustomUserDetailsService.java
package com.jwt.services; import com.jwt.model.CustomUserDetails; import com.jwt.model.User; import com.jwt.repo.UserRepository; 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 java.util.ArrayList; @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { final User user = this.userRepository.findByUsername(userName); if (user == null) { throw new UsernameNotFoundException("User not found !!"); } else { return new CustomUserDetails(user); } } }