├── .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 |
--------------------------------------------------------------------------------