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