build.gradle
plugins { id 'java' id 'org.springframework.boot' version '2.0.5.RELEASE' id 'io.spring.dependency-management' version '1.0.7.RELEASE' id "io.freefair.lombok" version "5.0.0-rc6" } repositories { jcenter() } sourceCompatibility = 1.8 targetCompatibility = 1.8 dependencies { implementation 'com.google.guava:guava:28.0-jre' testImplementation 'junit:junit:4.12' implementation 'org.springframework.boot:spring-boot-dependencies:2.0.5.RELEASE' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.sprd "net.ltgt.apt" version "0.21"ingframework.boot:spring-boot-starter-test' implementation 'com.h2database:h2:1.3.148' implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.2.6.RELEASE' implementation 'org.springframework.boot:spring-boot-starter-security:2.2.6.RELEASE' //implementation 'io.jsonwebtoken:jjwt:0.9.1' implementation 'io.jsonwebtoken:jjwt-api:0.10.2' implementation 'io.jsonwebtoken:jjwt-impl:0.10.2' implementation 'io.jsonwebtoken:jjwt-jackson:0.10.5' components { withModule('org.springframework:spring-beans') { allVariants { withDependencyConstraints { it.findAll { it.name == 'snakeyaml' }.each { it.version { strictly '1.19' } } } } } } } bootJar { mainClassName = 'GradleDemo.App' } task runJar{ dependsOn 'assemble' dependsOn 'jar' doLast{ javaexec { main="-jar"; args = [ "build/libs/"+rootProject.name+".jar" ] } } }
SpringSecurityJwtExampleApplication.java (Main Class)
package com.javatechie.jwt.api; import com.javatechie.jwt.api.entity.User; import com.javatechie.jwt.api.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import javax.annotation.PostConstruct; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @SpringBootApplication public class SpringSecurityJwtExampleApplication { @Autowired private UserRepository repository; @PostConstruct public void initUsers() { List<User> users = Stream.of( new User(101, "tyson", "password", "tyson@gmail.com"), new User(102, "user1", "pwd1", "user1@gmail.com"), new User(103, "user2", "pwd2", "user2@gmail.com"), new User(104, "user3", "pwd3", "user3@gmail.com") ).collect(Collectors.toList()); repository.saveAll(users); } public static void main(String[] args) { SpringApplication.run(SpringSecurityJwtExampleApplication.class, args); } }
SecurityConfig.java (Security Config file)
package com.javatechie.jwt.api.config; import com.javatechie.jwt.api.filter.JwtFilter; import com.javatechie.jwt.api.service.CustomUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserDetailsService userDetailsService; @Autowired private JwtFilter jwtFilter; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } @Bean public PasswordEncoder passwordEncoder(){ return NoOpPasswordEncoder.getInstance(); } @Bean(name = BeanIds.AUTHENTICATION_MANAGER) @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable().authorizeRequests().antMatchers("/authenticate") .permitAll().anyRequest().authenticated() .and().exceptionHandling().and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);; } }
AuthRequest.java (Requested JSON format as Bean)
package com.javatechie.jwt.api.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class AuthRequest { private String userName; private String password; }
User.java (JPA tables for H2 db)
package com.javatechie.jwt.api.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "USER_TBL") public class User { @Id private int id; private String userName; private String password; private String email; }
UserRepository.java (Support for JPA)
package com.javatechie.jwt.api.repository; import com.javatechie.jwt.api.entity.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User,Integer> { User findByUserName(String username); }
CustomUserDetailsService.java (UserDetails implementation)
package com.javatechie.jwt.api.service; import com.javatechie.jwt.api.entity.User; import com.javatechie.jwt.api.repository.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 repository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = repository.findByUserName(username); return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), new ArrayList<>()); } }
JwtUtil.java (Utility for JWT)
package com.javatechie.jwt.api.util; import java.security.Key; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.function.Function; import javax.annotation.PostConstruct; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; @Service public class JwtUtil { private Key key; @PostConstruct public void init() { key = Keys.secretKeyFor(SignatureAlgorithm.HS256); } 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(key).parseClaimsJws(token).getBody(); } private Boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } public String generateToken(String username) { Map<String, Object> claims = new HashMap<>(); return createToken(claims, username); } // @formatter:off 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(key).compact(); } // @formatter:on public Boolean validateToken(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } }
JwtFilter.java (Filter that will validate JWT on each request)
package com.javatechie.jwt.api.filter; import com.javatechie.jwt.api.service.CustomUserDetailsService; import com.javatechie.jwt.api.util.JwtUtil; 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.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 JwtFilter extends OncePerRequestFilter { @Autowired private JwtUtil jwtUtil; @Autowired private CustomUserDetailsService service; @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { String authorizationHeader = httpServletRequest.getHeader("Authorization"); String token = null; String userName = null; if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { token = authorizationHeader.substring(7); userName = jwtUtil.extractUsername(token); } if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = service.loadUserByUsername(userName); if (jwtUtil.validateToken(token, userDetails)) { UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); usernamePasswordAuthenticationToken .setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } } filterChain.doFilter(httpServletRequest, httpServletResponse); } }
WelcomeController.java (Rest Controller)
package com.javatechie.jwt.api.controller; import com.javatechie.jwt.api.entity.AuthRequest; import com.javatechie.jwt.api.util.JwtUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class WelcomeController { @Autowired private JwtUtil jwtUtil; @Autowired private AuthenticationManager authenticationManager; @GetMapping("/") public String welcome() { return "Welcome to Heapwizard.com !!"; } @PostMapping("/authenticate") public String generateToken(@RequestBody AuthRequest authRequest) throws Exception { try { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authRequest.getUserName(), authRequest.getPassword()) ); } catch (Exception ex) { throw new Exception("inavalid username/password"); } return jwtUtil.generateToken(authRequest.getUserName()); } }
Getting the token using (/authenticate) using username and passoword
Using the token in the Request Header for the request
If the token is incorrect :