├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java ├── Bootstrapper └── AppModule.java ├── Controller └── AuthController.java ├── DataAccess ├── ISignupRepository.java ├── IUserRepository.java ├── SignupRepositoryImpl.java └── UserRepositoryImpl.java ├── Model ├── Token.java └── UserModel.java ├── RedisProvider ├── IRedis.java └── RedisImpl.java ├── Server.java ├── Service ├── IJwtTokenService.java ├── JwtTokenServiceImpl.java └── ServerConfiguration.java ├── Util ├── Constant.java ├── IKeyGenerator.java ├── IKeyGeneratorImpl.java ├── ITimeProvider.java ├── JsonTransformer.java └── TimeProviderImpl.java └── Validation ├── ITokenValidator.java └── TokenValidator.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 2 | 3 | *.iml 4 | 5 | ## Directory-based project format: 6 | .idea/ 7 | # if you remove the above rule, at least ignore the following: 8 | 9 | # User-specific stuff: 10 | # .idea/workspace.xml 11 | # .idea/tasks.xml 12 | # .idea/dictionaries 13 | 14 | # Sensitive or high-churn files: 15 | # .idea/dataSources.ids 16 | # .idea/dataSources.xml 17 | # .idea/sqlDataSources.xml 18 | # .idea/dynamic.xml 19 | # .idea/uiDesigner.xml 20 | 21 | # Gradle: 22 | # .idea/gradle.xml 23 | # .idea/libraries 24 | 25 | # Mongo Explorer plugin: 26 | # .idea/mongoSettings.xml 27 | 28 | ## File-based project format: 29 | *.ipr 30 | *.iws 31 | 32 | ## Plugin-specific files: 33 | 34 | # IntelliJ 35 | /out/ 36 | 37 | # mpeltonen/sbt-idea plugin 38 | .idea_modules/ 39 | 40 | # JIRA plugin 41 | atlassian-ide-plugin.xml 42 | 43 | # Crashlytics plugin (for Android Studio and IntelliJ) 44 | com_crashlytics_export_strings.xml 45 | crashlytics.properties 46 | crashlytics-build.properties 47 | 48 | 49 | *.class 50 | 51 | # Mobile Tools for Java (J2ME) 52 | .mtj.tmp/ 53 | 54 | # Package Files # 55 | *.jar 56 | *.war 57 | *.ear 58 | 59 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 60 | hs_err_pid* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 gökhan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JSON Web Token Authentication with SparkFramework 2 | ============== 3 | 4 | This is a [Spark](http://sparkjava.com/) based authentication server. The goal is to take care of 5 | authentication/registration/ using a REST API. 6 | 7 | ## Installation 8 | ------------ 9 | Be sure install **them!** 10 | - **Java 8** 11 | - **Redis** 12 | 13 | Redis run on default port. 14 | 15 | ## How do I use it? 16 | 17 | Install maven dependency and launch the **server**. 18 | 19 | ### Register (POST /register) 20 | ```java 21 | Parameters raw (json body) 22 | { 23 | "username": The email of the user 24 | "password": The password of the user 25 | } 26 | ``` 27 | 28 | ### Token (POST /token) 29 | ```java 30 | Parameters raw (json body) 31 | 32 | { 33 | "username": The email of the user 34 | "password": The password of the user 35 | } 36 | ``` 37 | 38 | ### Protected Message (POST /protected/deneme) 39 | ```java 40 | HEADER 41 | 42 | { 43 | "X-API-TOKEN": TOKEN 44 | } 45 | ``` 46 | 47 | ## FORK IT :) 48 | 49 | 50 | ### TODO 51 | - [ ] Unit and Integration Test 52 | - [ ] Hash or Encrypted password 53 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | spark-jwt-auth 8 | spark-jwt-auth 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | 15 | com.sparkjava 16 | spark-core 17 | 2.2 18 | 19 | 20 | 21 | 22 | com.google.code.gson 23 | gson 24 | 2.3.1 25 | 26 | 27 | 28 | com.google.guava 29 | guava 30 | 18.0 31 | 32 | 33 | 34 | 35 | com.google.inject 36 | guice 37 | 3.0 38 | 39 | 40 | 41 | io.jsonwebtoken 42 | jjwt 43 | 0.5.1 44 | 45 | 46 | 47 | redis.clients 48 | jedis 49 | 2.7.2 50 | jar 51 | compile 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/main/java/Bootstrapper/AppModule.java: -------------------------------------------------------------------------------- 1 | package Bootstrapper; 2 | 3 | import DataAccess.ISignupRepository; 4 | import DataAccess.SignupRepositoryImpl; 5 | import DataAccess.IUserRepository; 6 | import DataAccess.UserRepositoryImpl; 7 | import RedisProvider.IRedis; 8 | import RedisProvider.RedisImpl; 9 | import Service.IJwtTokenService; 10 | import Service.JwtTokenServiceImpl; 11 | import Util.TimeProviderImpl; 12 | import Util.ITimeProvider; 13 | import Util.IKeyGenerator; 14 | import Util.IKeyGeneratorImpl; 15 | import Validation.ITokenValidator; 16 | import Validation.TokenValidator; 17 | import com.google.inject.AbstractModule; 18 | import com.google.inject.Singleton; 19 | 20 | /** 21 | * Created by previousdeveloper on 14.09.2015. 22 | */ 23 | public class AppModule extends AbstractModule { 24 | @Override 25 | protected void configure() { 26 | 27 | bind(ITimeProvider.class).to(TimeProviderImpl.class).in(Singleton.class); 28 | bind(IKeyGenerator.class).to(IKeyGeneratorImpl.class).in(Singleton.class); 29 | bind(IJwtTokenService.class).to(JwtTokenServiceImpl.class).in(Singleton.class); 30 | bind(ITokenValidator.class).to(TokenValidator.class).in(Singleton.class); 31 | bind(IRedis.class).to(RedisImpl.class); 32 | bind(ISignupRepository.class).to(SignupRepositoryImpl.class).asEagerSingleton(); 33 | bind(IUserRepository.class).to(UserRepositoryImpl.class).asEagerSingleton(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/Controller/AuthController.java: -------------------------------------------------------------------------------- 1 | package Controller; 2 | 3 | import Bootstrapper.AppModule; 4 | import DataAccess.ISignupRepository; 5 | import Model.UserModel; 6 | import Service.IJwtTokenService; 7 | import Validation.ITokenValidator; 8 | import com.google.gson.Gson; 9 | import com.google.gson.JsonSyntaxException; 10 | import com.google.inject.Guice; 11 | import com.google.inject.Injector; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | import static spark.Spark.*; 17 | 18 | /** 19 | * Created by previousdeveloper on 14.09.2015. 20 | */ 21 | 22 | public class AuthController { 23 | 24 | public AuthController() { 25 | 26 | 27 | Injector injector = Guice.createInjector(new AppModule()); 28 | ISignupRepository signupRepository = injector.getInstance(ISignupRepository.class); 29 | IJwtTokenService jwtAuthService = injector.getInstance(IJwtTokenService.class); 30 | ITokenValidator tokenValidation = injector.getInstance(ITokenValidator.class); 31 | 32 | //TODO: DI CONTAINER 33 | Gson gson = new Gson(); 34 | 35 | before("/protected/*", (request, response) -> { 36 | 37 | String xApiToken = request.headers("X-API-TOKEN"); 38 | 39 | 40 | if (xApiToken != null) { 41 | boolean result = tokenValidation.validate(xApiToken); 42 | 43 | if (!result) { 44 | halt(401, "token expired"); 45 | } 46 | 47 | } else { 48 | halt(401, "header token not found"); 49 | } 50 | 51 | }); 52 | 53 | post("/token", (request, response) -> { 54 | 55 | UserModel userModel = null; 56 | String token = null; 57 | String result = null; 58 | 59 | Map keyValuePair = null; 60 | 61 | try { 62 | 63 | userModel = gson.fromJson(request.body(), UserModel.class); 64 | String username = userModel.getUsername(); 65 | String password = userModel.getPassword(); 66 | if (username != null && password != null) { 67 | token = jwtAuthService.tokenGenerator(username, password); 68 | 69 | 70 | keyValuePair = new HashMap<>(); 71 | keyValuePair.put("token", token); 72 | 73 | result = gson.toJson(keyValuePair); 74 | } 75 | 76 | } catch (JsonSyntaxException e) { 77 | e.printStackTrace(); 78 | response.status(400); 79 | 80 | return "INVALID JSON"; 81 | } 82 | 83 | 84 | return result; 85 | }); 86 | 87 | post("/register", (request, response) -> { 88 | UserModel userModel = null; 89 | Map user; 90 | 91 | try { 92 | 93 | userModel = gson.fromJson(request.body(), UserModel.class); 94 | signupRepository.saveUser(userModel); 95 | user = new HashMap<>(); 96 | user.put("Registered:", userModel.getUsername()); 97 | 98 | } catch (JsonSyntaxException e) { 99 | e.printStackTrace(); 100 | response.status(400); 101 | 102 | return "INVALID JSON"; 103 | } 104 | 105 | return gson.toJson(user); 106 | }); 107 | 108 | post("/protected/deneme", (request, response) -> { 109 | 110 | return "SELAM BASARILI GIRIS YAPTIN :)"; 111 | }); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/DataAccess/ISignupRepository.java: -------------------------------------------------------------------------------- 1 | package DataAccess; 2 | 3 | import Model.UserModel; 4 | 5 | /** 6 | * Created by previousdeveloper on 14.09.2015. 7 | */ 8 | public interface ISignupRepository { 9 | 10 | void saveUser(UserModel userModel); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/DataAccess/IUserRepository.java: -------------------------------------------------------------------------------- 1 | package DataAccess; 2 | 3 | import Model.UserModel; 4 | 5 | /** 6 | * Created by previousdeveloper on 16.09.2015. 7 | */ 8 | public interface IUserRepository { 9 | 10 | UserModel getUser(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/DataAccess/SignupRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package DataAccess; 2 | 3 | import Model.UserModel; 4 | import RedisProvider.IRedis; 5 | import Util.JsonTransformer; 6 | import com.google.inject.Inject; 7 | 8 | /** 9 | * Created by previousdeveloper on 14.09.2015. 10 | */ 11 | public class SignupRepositoryImpl implements ISignupRepository { 12 | 13 | private IRedis redis; 14 | 15 | @Inject 16 | public SignupRepositoryImpl(IRedis redis) { 17 | 18 | this.redis = redis; 19 | } 20 | 21 | public void saveUser(UserModel userModel) { 22 | 23 | String value = null; 24 | 25 | try { 26 | value = new JsonTransformer().render(userModel); 27 | redis.set("user", value); 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | } 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/DataAccess/UserRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package DataAccess; 2 | 3 | import Model.UserModel; 4 | import RedisProvider.IRedis; 5 | import com.google.gson.Gson; 6 | import com.google.inject.Inject; 7 | 8 | /** 9 | * Created by previousdeveloper on 16.09.2015. 10 | */ 11 | public class UserRepositoryImpl implements IUserRepository { 12 | 13 | private IRedis redis; 14 | private Gson gson; 15 | private UserModel userModel = null; 16 | 17 | @Inject 18 | public UserRepositoryImpl(IRedis redis) { 19 | this.redis = redis; 20 | gson = new Gson(); 21 | } 22 | 23 | @Override 24 | public UserModel getUser() { 25 | 26 | String user = redis.get("user"); 27 | this.userModel = gson.fromJson(user, UserModel.class); 28 | 29 | return userModel; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/Model/Token.java: -------------------------------------------------------------------------------- 1 | package Model; 2 | 3 | /** 4 | * Created by previousdeveloper on 16.09.2015. 5 | */ 6 | public class Token { 7 | 8 | private String jwtToken; 9 | 10 | public String getJwtToken() { 11 | return jwtToken; 12 | } 13 | 14 | public void setJwtToken(String jwtToken) { 15 | this.jwtToken = jwtToken; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/Model/UserModel.java: -------------------------------------------------------------------------------- 1 | package Model; 2 | 3 | /** 4 | * Created by previousdeveloper on 14.09.2015. 5 | */ 6 | public class UserModel { 7 | private String username; 8 | private String password; 9 | 10 | public String getUsername() { 11 | return username; 12 | } 13 | 14 | public void setUsername(String username) { 15 | this.username = username; 16 | } 17 | 18 | public String getPassword() { 19 | return password; 20 | } 21 | 22 | public void setPassword(String password) { 23 | this.password = password; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "UserModel{" + 29 | "username='" + username + '\'' + 30 | ", password='" + password + '\'' + 31 | '}'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/RedisProvider/IRedis.java: -------------------------------------------------------------------------------- 1 | package RedisProvider; 2 | 3 | /** 4 | * Created by previousdeveloper on 14.09.2015. 5 | */ 6 | public interface IRedis { 7 | 8 | String set(String key, String value); 9 | 10 | String get(String key); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/RedisProvider/RedisImpl.java: -------------------------------------------------------------------------------- 1 | package RedisProvider; 2 | 3 | import redis.clients.jedis.Jedis; 4 | 5 | /** 6 | * Created by previousdeveloper on 14.09.2015. 7 | */ 8 | public class RedisImpl implements IRedis { 9 | 10 | Jedis jedisConnector = new Jedis("localhost"); 11 | 12 | @Override 13 | public String set(String key, String value) { 14 | return jedisConnector.set(key, value); 15 | } 16 | 17 | 18 | @Override 19 | public String get(String key) { 20 | return jedisConnector.get(key); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/Server.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by previousdeveloper on 14.09.2015. 3 | */ 4 | 5 | public class Server { 6 | 7 | public static void main(String[] args) { 8 | 9 | new Controller.AuthController(); 10 | 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/Service/IJwtTokenService.java: -------------------------------------------------------------------------------- 1 | package Service; 2 | 3 | /** 4 | * Created by previousdeveloper on 14.09.2015. 5 | */ 6 | public interface IJwtTokenService { 7 | 8 | String tokenGenerator(String username, String password); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/Service/JwtTokenServiceImpl.java: -------------------------------------------------------------------------------- 1 | package Service; 2 | 3 | import Util.Constant; 4 | import Util.ITimeProvider; 5 | import com.google.inject.Inject; 6 | import io.jsonwebtoken.Jwts; 7 | import io.jsonwebtoken.SignatureAlgorithm; 8 | 9 | public class JwtTokenServiceImpl implements IJwtTokenService { 10 | 11 | 12 | private ITimeProvider currentTime; 13 | 14 | @Inject 15 | public JwtTokenServiceImpl(ITimeProvider currentTime) { 16 | 17 | this.currentTime = currentTime; 18 | } 19 | 20 | public String tokenGenerator(String username, String password) { 21 | 22 | 23 | SignatureAlgorithm hs512 = SignatureAlgorithm.HS512; 24 | 25 | 26 | String token = Jwts.builder() 27 | .claim("username", username) 28 | .claim("password", password) 29 | //Todo:Config 30 | .claim("expireTime", currentTime.getCurrentTime() + Constant.TOKEN_TIMEOUT_INMILISECOND) 31 | .signWith(hs512, Constant.JWT_SECRET) 32 | .compact(); 33 | 34 | return token; 35 | } 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/Service/ServerConfiguration.java: -------------------------------------------------------------------------------- 1 | package Service; 2 | 3 | /** 4 | * Created by previousdeveloper on 14.09.2015. 5 | */ 6 | public class ServerConfiguration { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/Util/Constant.java: -------------------------------------------------------------------------------- 1 | package Util; 2 | 3 | /** 4 | * Created by previousdeveloper on 15.09.2015. 5 | */ 6 | public class Constant { 7 | 8 | public final static String JWT_SECRET = "1mmkkj23jkj12cmk213lx13ml"; 9 | public final static long TOKEN_TIMEOUT_INMILISECOND = 80000; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/Util/IKeyGenerator.java: -------------------------------------------------------------------------------- 1 | package Util; 2 | 3 | import java.security.Key; 4 | 5 | /** 6 | * Created by previousdeveloper on 14.09.2015. 7 | */ 8 | public interface IKeyGenerator { 9 | 10 | Key getKey(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/Util/IKeyGeneratorImpl.java: -------------------------------------------------------------------------------- 1 | package Util; 2 | 3 | import io.jsonwebtoken.impl.crypto.MacProvider; 4 | 5 | import java.security.Key; 6 | 7 | /** 8 | * Created by previousdeveloper on 14.09.2015. 9 | */ 10 | public class IKeyGeneratorImpl implements IKeyGenerator { 11 | 12 | @Override 13 | public Key getKey() { 14 | 15 | return MacProvider.generateKey(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/Util/ITimeProvider.java: -------------------------------------------------------------------------------- 1 | package Util; 2 | 3 | /** 4 | * Created by previousdeveloper on 14.09.2015. 5 | */ 6 | public interface ITimeProvider { 7 | 8 | Long getCurrentTime(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/Util/JsonTransformer.java: -------------------------------------------------------------------------------- 1 | package Util; 2 | 3 | import com.google.gson.Gson; 4 | import spark.ResponseTransformer; 5 | 6 | /** 7 | * Created by previousdeveloper on 14.09.2015. 8 | */ 9 | public class JsonTransformer implements ResponseTransformer { 10 | 11 | private Gson gson = new Gson(); 12 | 13 | public String render(Object model) throws Exception { 14 | 15 | return gson.toJson(model); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/Util/TimeProviderImpl.java: -------------------------------------------------------------------------------- 1 | package Util; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.ZoneId; 5 | 6 | /** 7 | * Created by previousdeveloper on 14.09.2015. 8 | */ 9 | public class TimeProviderImpl implements ITimeProvider { 10 | 11 | public Long getCurrentTime() { 12 | 13 | long result = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); 14 | 15 | return result; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/Validation/ITokenValidator.java: -------------------------------------------------------------------------------- 1 | package Validation; 2 | 3 | /** 4 | * Created by previousdeveloper on 15.09.2015. 5 | */ 6 | public interface ITokenValidator { 7 | 8 | boolean validate(String token); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/Validation/TokenValidator.java: -------------------------------------------------------------------------------- 1 | package Validation; 2 | 3 | import DataAccess.IUserRepository; 4 | import RedisProvider.IRedis; 5 | import Util.Constant; 6 | import Util.ITimeProvider; 7 | import com.google.inject.Inject; 8 | import io.jsonwebtoken.Jwts; 9 | 10 | /** 11 | * Created by previousdeveloper on 14.09.2015. 12 | */ 13 | public class TokenValidator implements ITokenValidator { 14 | 15 | 16 | private IRedis redis; 17 | private IUserRepository IUserRepository; 18 | private ITimeProvider timeProvider; 19 | 20 | @Inject 21 | public TokenValidator(IRedis redis, IUserRepository IUserRepository, ITimeProvider timeProvider) { 22 | 23 | this.redis = redis; 24 | 25 | this.IUserRepository = IUserRepository; 26 | this.timeProvider = timeProvider; 27 | } 28 | 29 | public boolean validate(String token) { 30 | 31 | boolean valid = false; 32 | 33 | 34 | Object username = Jwts.parser().setSigningKey(Constant.JWT_SECRET).parseClaimsJws(token) 35 | .getBody().get("username"); 36 | 37 | Object password = Jwts.parser().setSigningKey(Constant.JWT_SECRET).parseClaimsJws(token) 38 | .getBody().get("password"); 39 | 40 | Object expireTime = Jwts.parser().setSigningKey(Constant.JWT_SECRET).parseClaimsJws(token) 41 | .getBody().get("expireTime"); 42 | 43 | Long currentTimeInMilisecond = timeProvider.getCurrentTime(); 44 | 45 | if (IUserRepository.getUser().getUsername().equals(username) && 46 | IUserRepository.getUser().getPassword().equals(password) && 47 | (Long) expireTime > currentTimeInMilisecond) { 48 | valid = true; 49 | } 50 | 51 | return valid; 52 | } 53 | } 54 | --------------------------------------------------------------------------------