├── .gitignore ├── README.md ├── docker-compose.yml ├── pom.xml └── src ├── main ├── java │ └── me │ │ └── aboullaite │ │ ├── SpringBootJwtApplication.java │ │ ├── config │ │ ├── CorsConfig.java │ │ └── JwtFilter.java │ │ ├── controller │ │ ├── SecureController.java │ │ └── UserController.java │ │ ├── dao │ │ └── UserDao.java │ │ ├── model │ │ └── User.java │ │ └── service │ │ ├── UserService.java │ │ └── impl │ │ └── UserServiceImpl.java └── resources │ └── application.yml └── test └── java └── me └── aboullaite └── SpringBootJwtApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | 3 | ### STS ### 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | 11 | ### IntelliJ IDEA ### 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | ### NetBeans ### 18 | nbproject/private/ 19 | build/ 20 | nbbuild/ 21 | dist/ 22 | nbdist/ 23 | .nb-gradle/ 24 | /data/ 25 | /target/ 26 | /mvnw 27 | /mvnw.cmd 28 | /.mvn/ 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring-boot-jwt 2 | 3 | This is an example project where a Spring REST API is secured using JSON Web Tokens. [Blog post on this subject](https://aboullaite.me/spring-boot-token-authentication-using-jwt) -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | phpmyadmin: 2 | image: corbinu/docker-phpmyadmin 3 | ports : 4 | - "8082:80" 5 | environment: 6 | - MYSQL_USERNAME=root 7 | - MYSQL_PASSWORD=98U5mJY566 8 | links: 9 | - database:mysql 10 | database: 11 | image: mysql:5.5 12 | ports: 13 | - "3334:3306" 14 | environment: 15 | - MYSQL_ROOT_PASSWORD=98U5mJY566 16 | - MYSQL_DATABASE=asfim 17 | - MYSQL_USER=asfim 18 | - MYSQL_PASSWORD=98U5mJY566 19 | volumes: 20 | - ./data:/var/lib/mysql -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | me.aboullaite 7 | spring_boot_jwt 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | spring_boot_jwt 12 | Demo project for Spring Boot and JWT 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.1.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-data-jpa 36 | 37 | 38 | 39 | mysql 40 | mysql-connector-java 41 | 42 | 43 | 44 | io.jsonwebtoken 45 | jjwt 46 | 0.6.0 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-test 52 | test 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-maven-plugin 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/main/java/me/aboullaite/SpringBootJwtApplication.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite; 2 | 3 | import me.aboullaite.config.JwtFilter; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | @SpringBootApplication 10 | public class SpringBootJwtApplication { 11 | 12 | @Bean 13 | public FilterRegistrationBean jwtFilter() { 14 | final FilterRegistrationBean registrationBean = new FilterRegistrationBean(); 15 | registrationBean.setFilter(new JwtFilter()); 16 | registrationBean.addUrlPatterns("/secure/*"); 17 | 18 | return registrationBean; 19 | } 20 | 21 | public static void main(String[] args) { 22 | SpringApplication.run(SpringBootJwtApplication.class, args); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/aboullaite/config/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite.config; 2 | 3 | 4 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.web.cors.CorsConfiguration; 8 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 9 | import org.springframework.web.filter.CorsFilter; 10 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 12 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 13 | 14 | @Configuration 15 | public class CorsConfig { 16 | 17 | @Bean 18 | public FilterRegistrationBean corsFilter() { 19 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 20 | CorsConfiguration config = new CorsConfiguration(); 21 | config.setAllowCredentials(true); 22 | config.addAllowedOrigin("*"); 23 | config.addAllowedHeader("*"); 24 | config.addAllowedMethod("OPTIONS"); 25 | config.addAllowedMethod("HEAD"); 26 | config.addAllowedMethod("GET"); 27 | config.addAllowedMethod("PUT"); 28 | config.addAllowedMethod("POST"); 29 | config.addAllowedMethod("DELETE"); 30 | config.addAllowedMethod("PATCH"); 31 | source.registerCorsConfiguration("/**", config); 32 | // return new CorsFilter(source); 33 | final FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); 34 | bean.setOrder(0); 35 | return bean; 36 | } 37 | 38 | @Bean 39 | public WebMvcConfigurer mvcConfigurer() { 40 | return new WebMvcConfigurerAdapter() { 41 | public void addCorsMappings(CorsRegistry registry) { 42 | registry.addMapping("/**").allowedMethods("GET", "PUT", "POST", "GET", "OPTIONS"); 43 | } 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/me/aboullaite/config/JwtFilter.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite.config; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.ServletRequest; 8 | import javax.servlet.ServletResponse; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | import org.springframework.web.filter.GenericFilterBean; 13 | 14 | import io.jsonwebtoken.Claims; 15 | import io.jsonwebtoken.Jwts; 16 | import io.jsonwebtoken.SignatureException; 17 | 18 | public class JwtFilter extends GenericFilterBean { 19 | 20 | public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) 21 | throws IOException, ServletException { 22 | 23 | final HttpServletRequest request = (HttpServletRequest) req; 24 | final HttpServletResponse response = (HttpServletResponse) res; 25 | final String authHeader = request.getHeader("authorization"); 26 | 27 | if ("OPTIONS".equals(request.getMethod())) { 28 | response.setStatus(HttpServletResponse.SC_OK); 29 | 30 | chain.doFilter(req, res); 31 | } else { 32 | 33 | if (authHeader == null || !authHeader.startsWith("Bearer ")) { 34 | throw new ServletException("Missing or invalid Authorization header"); 35 | } 36 | 37 | final String token = authHeader.substring(7); 38 | 39 | try { 40 | final Claims claims = Jwts.parser().setSigningKey("secretkey").parseClaimsJws(token).getBody(); 41 | request.setAttribute("claims", claims); 42 | } catch (final SignatureException e) { 43 | throw new ServletException("Invalid token"); 44 | } 45 | 46 | chain.doFilter(req, res); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/me/aboullaite/controller/SecureController.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.RequestBody; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import me.aboullaite.model.User; 10 | import me.aboullaite.service.UserService; 11 | 12 | @RestController 13 | @RequestMapping("/secure") 14 | public class SecureController { 15 | 16 | @Autowired 17 | private UserService userService; 18 | 19 | @RequestMapping("/user/users") 20 | public String loginSuccess() { 21 | return "Login Successful!"; 22 | } 23 | 24 | @RequestMapping(value = "/user/email", method = RequestMethod.POST) 25 | public User findByEmail(@RequestBody String email) { 26 | return userService.findByEmail(email); 27 | } 28 | 29 | @RequestMapping(value = "/user/update", method = RequestMethod.POST) 30 | public User updateUser(@RequestBody User user) { 31 | return userService.save(user); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/aboullaite/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite.controller; 2 | 3 | import java.util.Date; 4 | 5 | import javax.servlet.ServletException; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.CrossOrigin; 9 | import org.springframework.web.bind.annotation.RequestBody; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestMethod; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | import me.aboullaite.model.User; 15 | import me.aboullaite.service.UserService; 16 | 17 | import io.jsonwebtoken.Jwts; 18 | import io.jsonwebtoken.SignatureAlgorithm; 19 | 20 | @CrossOrigin(origins = "http://localhost", maxAge = 3600) 21 | @RestController 22 | @RequestMapping("/user") 23 | public class UserController { 24 | 25 | @Autowired 26 | private UserService userService; 27 | 28 | @RequestMapping(value = "/register", method = RequestMethod.POST) 29 | public User registerUser(@RequestBody User user) { 30 | return userService.save(user); 31 | } 32 | 33 | @RequestMapping(value = "/login", method = RequestMethod.POST) 34 | public String login(@RequestBody User login) throws ServletException { 35 | 36 | String jwtToken = ""; 37 | 38 | if (login.getEmail() == null || login.getPassword() == null) { 39 | throw new ServletException("Please fill in username and password"); 40 | } 41 | 42 | String email = login.getEmail(); 43 | String password = login.getPassword(); 44 | 45 | User user = userService.findByEmail(email); 46 | 47 | if (user == null) { 48 | throw new ServletException("User email not found."); 49 | } 50 | 51 | String pwd = user.getPassword(); 52 | 53 | if (!password.equals(pwd)) { 54 | throw new ServletException("Invalid login. Please check your name and password."); 55 | } 56 | 57 | jwtToken = Jwts.builder().setSubject(email).claim("roles", "user").setIssuedAt(new Date()) 58 | .signWith(SignatureAlgorithm.HS256, "secretkey").compact(); 59 | 60 | return jwtToken; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/me/aboullaite/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite.dao; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import me.aboullaite.model.User; 7 | 8 | @Repository 9 | public interface UserDao extends CrudRepository { 10 | User save(User user); 11 | 12 | User findByEmail(String email); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/aboullaite/model/User.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite.model; 2 | 3 | import java.util.Date; 4 | import javax.persistence.Entity; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | 9 | import org.hibernate.annotations.CreationTimestamp; 10 | 11 | @Entity 12 | public class User { 13 | 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.AUTO) 16 | private Long userId; 17 | private String firstName; 18 | private String lastName; 19 | private String email; 20 | private String password; 21 | 22 | @CreationTimestamp 23 | private Date created; 24 | 25 | public Long getUserId() { 26 | return userId; 27 | } 28 | 29 | public void setUserId(Long userId) { 30 | this.userId = userId; 31 | } 32 | 33 | public String getFirstName() { 34 | return firstName; 35 | } 36 | 37 | public void setFirstName(String firstName) { 38 | this.firstName = firstName; 39 | } 40 | 41 | public String getLastName() { 42 | return lastName; 43 | } 44 | 45 | public void setLastName(String lastName) { 46 | this.lastName = lastName; 47 | } 48 | 49 | public String getEmail() { 50 | return email; 51 | } 52 | 53 | public void setEmail(String email) { 54 | this.email = email; 55 | } 56 | 57 | public String getPassword() { 58 | return password; 59 | } 60 | 61 | public void setPassword(String password) { 62 | this.password = password; 63 | } 64 | 65 | public Date getCreated() { 66 | return created; 67 | } 68 | 69 | public void setCreated(Date created) { 70 | this.created = created; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/me/aboullaite/service/UserService.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite.service; 2 | 3 | import me.aboullaite.model.User; 4 | 5 | public interface UserService { 6 | User save(User user); 7 | 8 | User findByEmail(String email); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/aboullaite/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite.service.impl; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Service; 5 | 6 | import me.aboullaite.dao.UserDao; 7 | import me.aboullaite.model.User; 8 | import me.aboullaite.service.UserService; 9 | 10 | @Service 11 | public class UserServiceImpl implements UserService { 12 | 13 | @Autowired 14 | private UserDao userDao; 15 | 16 | public User save(User user) { 17 | return userDao.save(user); 18 | } 19 | 20 | public User findByEmail(String email) { 21 | return userDao.findByEmail(email); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | url: jdbc:mysql://localhost:3334/spring-jwt?useSSL=false 4 | username: spring-jwt 5 | password: 98U5mJY566 6 | jpa: 7 | show-sql: true 8 | hibernate: 9 | ddl-auto: create-drop 10 | naming: 11 | strategy: org.hibernate.cfg.ImprovedNamingStrategy 12 | properties: 13 | hibernate: 14 | dialect: org.hibernate.dialect.MySQL5Dialect 15 | -------------------------------------------------------------------------------- /src/test/java/me/aboullaite/SpringBootJwtApplicationTests.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringBootJwtApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | --------------------------------------------------------------------------------