├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java └── com └── r6lab └── sparkjava └── jwt ├── AuthFilter.java ├── BlacklistedTokenRepository.java ├── SparkJwtExample.java ├── TokenService.java ├── controller ├── AbstractTokenController.java ├── AuthController.java └── UserController.java └── user ├── Role.java ├── User.java ├── UserPrincipal.java └── UserService.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.idea/ 3 | *target/ 4 | 5 | # Compiled class file 6 | *.class 7 | 8 | # Log file 9 | *.log 10 | 11 | # BlueJ files 12 | *.ctxt 13 | 14 | # Mobile Tools for Java (J2ME) 15 | .mtj.tmp/ 16 | 17 | # Package Files # 18 | *.jar 19 | *.war 20 | *.nar 21 | *.ear 22 | *.zip 23 | *.tar.gz 24 | *.rar 25 | 26 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 rjozefowicz 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sparkjava-jwt 2 | Example SparkJava - JWT integration 3 | 4 | # tech 5 | ![](https://img.shields.io/badge/java%208-%E2%9C%93-blue.svg) 6 | ![](https://img.shields.io/badge/sparkjava-✓-blue.svg) 7 | ![](https://img.shields.io/badge/jwt-✓-blue.svg) 8 | 9 | ## Public available endpoints 10 | 11 | | ENDPOINT | HTTP METHOD | PARAMS | DESCRIPTION | 12 | | ------ | ------ | ------ | ------ | 13 | | /auth/register | POST | JSON body mandatory fields: userName, password. Additional fields firstName, secondName | New user registration | 14 | | /auth/login | POST | JSON body mandatory fields: userName, password | User login | 15 | 16 | ## Additional JWT endpoints 17 | 18 | **HTTP Header:** *Authorization: Bearer JWTToken* 19 | 20 | | ENDPOINT | HTTP METHOD | PARAMS | DESCRIPTION | 21 | | ------ | ------ | ------ | ------ | 22 | | /auth/token | POST | | JWT token refresh | 23 | | /auth/logout | POST | | JWT token revocation | 24 | | /auth/me | GET | | User details | 25 | 26 | ## Roles 27 | 28 | #### Predefined user roles: 29 | * ADMIN 30 | * MANAGER 31 | * DEVELOPER 32 | 33 | #### Endpoints for Role management: 34 | 35 | ENDPOINT | HTTP METHOD | PARAMS | DESCRIPTION | 36 | | ------ | ------ | ------ | ------ | 37 | | /auth/roles | POST | JSON body mandatory fields: userName, role | Add new Role to user | 38 | | /auth/roles | DELETE | JSON body mandatory fields: userName, role | Revoke Role from User | 39 | 40 | ## Admin user 41 | 42 | Predefined Admin user (admin/admin) 43 | 44 | ## Additional 45 | 46 | Cron job (every minute) to clean up revoked JWT Tokens 47 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.r6lab 8 | sparkjava-jwt 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 8 17 | 8 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | com.sparkjava 26 | spark-core 27 | 2.7.2 28 | 29 | 30 | io.jsonwebtoken 31 | jjwt 32 | 0.7.0 33 | 34 | 35 | com.google.code.gson 36 | gson 37 | 2.8.5 38 | 39 | 40 | org.mindrot 41 | jbcrypt 42 | 0.4 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/AuthFilter.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt; 2 | 3 | import spark.Filter; 4 | import spark.Request; 5 | import spark.Response; 6 | 7 | import java.util.logging.Logger; 8 | 9 | import static spark.Spark.halt; 10 | 11 | public class AuthFilter implements Filter { 12 | 13 | private static final Logger LOG = Logger.getLogger(AuthFilter.class.getName()); 14 | 15 | private static final String TOKEN_PREFIX = "Bearer"; 16 | private static final String LOGIN_ENDPOINT = "/login"; 17 | private static final String REGISTRATION_ENDPOINT = "/registration"; 18 | private static final String HTTP_POST = "POST"; 19 | 20 | private final String authEndpointPrefix; 21 | 22 | private TokenService tokenService; 23 | 24 | public AuthFilter(String authEndpointPrefix, TokenService tokenService) { 25 | this.authEndpointPrefix = authEndpointPrefix; 26 | this.tokenService = tokenService; 27 | } 28 | 29 | public void handle(Request request, Response response) { 30 | if (!isLoginRequest(request) && !isRegistrationRequest(request)) { 31 | String authorizationHeader = request.headers("Authorization"); 32 | if (authorizationHeader == null) { 33 | LOG.warning("Missing Authorization header"); 34 | halt(401); 35 | } else if (!tokenService.validateToken(authorizationHeader.replace(TOKEN_PREFIX, ""))) { 36 | LOG.warning("Expired token " + authorizationHeader); 37 | halt(401); 38 | } 39 | } 40 | } 41 | 42 | private boolean isLoginRequest(Request request) { 43 | return request.uri().equals(authEndpointPrefix + LOGIN_ENDPOINT) && request.requestMethod().equals(HTTP_POST); 44 | } 45 | 46 | private boolean isRegistrationRequest(Request request) { 47 | return request.uri().equals(authEndpointPrefix + REGISTRATION_ENDPOINT) && request.requestMethod().equals(HTTP_POST); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/BlacklistedTokenRepository.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.CopyOnWriteArrayList; 5 | 6 | public final class BlacklistedTokenRepository { 7 | 8 | private final List tokens = new CopyOnWriteArrayList<>(); 9 | 10 | public void removeExpired() { 11 | final long currentTimestamp = System.currentTimeMillis(); 12 | tokens.stream().filter(token -> token.getExpirationDate() < currentTimestamp).forEach(token -> { 13 | System.out.println("Removing token " + token.getToken()); 14 | tokens.remove(token); 15 | }); 16 | } 17 | 18 | public void addToken(String token, long expirationDate) { 19 | tokens.add(new BlacklistedTokenHolder(token, expirationDate)); 20 | } 21 | 22 | public boolean isTokenBlacklisted(String token) { 23 | return tokens.stream().filter(b -> b.getToken().equals(token)).findAny().isPresent(); 24 | } 25 | 26 | private final class BlacklistedTokenHolder { 27 | 28 | private final String token; 29 | private final long expirationDate; 30 | 31 | public BlacklistedTokenHolder(String token, long expirationDate) { 32 | this.token = token; 33 | this.expirationDate = expirationDate; 34 | } 35 | 36 | public String getToken() { 37 | return token; 38 | } 39 | 40 | public long getExpirationDate() { 41 | return expirationDate; 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/SparkJwtExample.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt; 2 | 3 | import com.google.gson.GsonBuilder; 4 | import com.r6lab.sparkjava.jwt.controller.AuthController; 5 | import com.r6lab.sparkjava.jwt.controller.UserController; 6 | import com.r6lab.sparkjava.jwt.user.UserService; 7 | import spark.Spark; 8 | 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.ScheduledExecutorService; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | public final class SparkJwtExample { 14 | 15 | private static final String SECRET_JWT = "secret_jwt"; 16 | 17 | private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor(); 18 | 19 | private final TokenService tokenService = new TokenService(SECRET_JWT); 20 | 21 | public void init() { 22 | 23 | new AuthController(new GsonBuilder().create(), new UserService(), tokenService).init(); 24 | new UserController(tokenService).init(); 25 | 26 | // PERIODIC TOKENS CLEAN UP 27 | EXECUTOR_SERVICE.scheduleAtFixedRate(() -> { 28 | System.out.println("Removing expired tokens"); 29 | tokenService.removeExpired(); 30 | }, 60, 60, TimeUnit.SECONDS); // every minute 31 | 32 | Spark.exception(Exception.class, (e, request, response) -> { 33 | System.err.println("Exception while processing request"); 34 | e.printStackTrace(); 35 | }); 36 | 37 | } 38 | 39 | // BOOTSTRAP 40 | public static void main(String[] args) { 41 | SparkJwtExample sparkJwtExample = new SparkJwtExample(); 42 | sparkJwtExample.init(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/TokenService.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt; 2 | 3 | import com.r6lab.sparkjava.jwt.user.Role; 4 | import com.r6lab.sparkjava.jwt.user.User; 5 | import com.r6lab.sparkjava.jwt.user.UserPrincipal; 6 | import io.jsonwebtoken.Claims; 7 | import io.jsonwebtoken.Jwts; 8 | import io.jsonwebtoken.SignatureAlgorithm; 9 | import io.jsonwebtoken.impl.DefaultClaims; 10 | 11 | import java.util.Date; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | public final class TokenService { 16 | 17 | private static final long EXPIRATION_TIME = 10 * 60 * 1000l; // 10 minutes 18 | private static final String ROLES = "roles"; 19 | 20 | private final String jwtSecretKey; 21 | 22 | private final BlacklistedTokenRepository blacklistedTokenRepository = new BlacklistedTokenRepository(); 23 | 24 | public TokenService(String jwtSecretKey) { 25 | this.jwtSecretKey = jwtSecretKey; 26 | } 27 | 28 | public final void removeExpired() { 29 | blacklistedTokenRepository.removeExpired(); 30 | } 31 | 32 | public final String newToken(User user) { 33 | DefaultClaims claims = new DefaultClaims(); 34 | claims.put(ROLES, user.getRoles()); 35 | claims.setSubject(user.getUsername()); 36 | return Jwts.builder() 37 | .setClaims(claims) 38 | .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) 39 | .signWith(SignatureAlgorithm.HS512, jwtSecretKey) 40 | .compact(); 41 | } 42 | 43 | public final void revokeToken(String token) { 44 | Date expirationDate = Jwts.parser() 45 | .setSigningKey(jwtSecretKey) 46 | .parseClaimsJws(token) 47 | .getBody() 48 | .getExpiration(); 49 | blacklistedTokenRepository.addToken(token, expirationDate.getTime()); 50 | } 51 | 52 | /** 53 | * throws ExpiredJwtException if token has expired 54 | * 55 | * @param token 56 | * @return 57 | */ 58 | public final UserPrincipal getUserPrincipal(String token) { 59 | Claims claims = Jwts.parser() 60 | .setSigningKey(jwtSecretKey) 61 | .parseClaimsJws(token) 62 | .getBody(); 63 | List roles = (List) claims.get(ROLES); 64 | return UserPrincipal.of(claims.getSubject(), roles.stream().map(role -> Role.valueOf(role)).collect(Collectors.toList())); 65 | } 66 | 67 | public final boolean isTokenBlacklisted(String token) { 68 | return blacklistedTokenRepository.isTokenBlacklisted(token); 69 | } 70 | 71 | public final boolean validateToken(String token) { 72 | if (!isTokenBlacklisted(token)) { 73 | try { 74 | getUserPrincipal(token); 75 | return true; 76 | } catch (Exception e) { 77 | return false; 78 | } 79 | } else { 80 | return false; 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/controller/AbstractTokenController.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt.controller; 2 | 3 | import com.r6lab.sparkjava.jwt.TokenService; 4 | import com.r6lab.sparkjava.jwt.user.Role; 5 | import com.r6lab.sparkjava.jwt.user.UserPrincipal; 6 | import spark.Request; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | public abstract class AbstractTokenController { 12 | 13 | private static final String TOKEN_PREFIX = "Bearer"; 14 | 15 | private final TokenService tokenService; 16 | 17 | public AbstractTokenController(TokenService tokenService) { 18 | this.tokenService = tokenService; 19 | } 20 | 21 | protected UserPrincipal getUserPrincipal(Request request) { 22 | String authorizationHeader = request.headers("Authorization"); 23 | String token = authorizationHeader.replace(TOKEN_PREFIX, ""); 24 | return tokenService.getUserPrincipal(token); 25 | } 26 | 27 | protected boolean hasRole(Request request, Role[] roles) { 28 | if (roles.length == 0) { 29 | return true; 30 | } 31 | List userRoles = getUserPrincipal(request).getRoles(); 32 | return userRoles.stream().filter(Arrays.asList(roles)::contains).findAny().isPresent(); 33 | } 34 | 35 | protected String getUserNameFromToken(Request request) { 36 | String authorizationHeader = request.headers("Authorization"); 37 | String token = authorizationHeader.replace(TOKEN_PREFIX, ""); 38 | return tokenService.getUserPrincipal(token).getUserName(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/controller/AuthController.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt.controller; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonObject; 5 | import com.r6lab.sparkjava.jwt.AuthFilter; 6 | import com.r6lab.sparkjava.jwt.TokenService; 7 | import com.r6lab.sparkjava.jwt.user.Role; 8 | import com.r6lab.sparkjava.jwt.user.User; 9 | import com.r6lab.sparkjava.jwt.user.UserService; 10 | import org.mindrot.jbcrypt.BCrypt; 11 | import spark.Request; 12 | import spark.Response; 13 | import spark.Spark; 14 | 15 | import java.io.IOException; 16 | import java.util.stream.Collectors; 17 | 18 | import static spark.Spark.before; 19 | import static spark.Spark.get; 20 | import static spark.Spark.halt; 21 | import static spark.Spark.post; 22 | 23 | public class AuthController extends AbstractTokenController { 24 | 25 | private static final String ROLE_PROPERTY = "role"; 26 | private static final String TOKEN_PREFIX = "Bearer"; 27 | private static final String AUTHORIZATION_HEADER = "Authorization"; 28 | private static final String USER_NAME_PROPERTY = "userName"; 29 | private static final String FIRST_NAME_PROPERTY = "firstName"; 30 | private static final String LAST_NAME_PROPERTY = "lastName"; 31 | private static final String PASSWORD_PROPERTY = "password"; 32 | private static final String AUTH_ENDPOINT_PREFIX = "/auth"; 33 | 34 | private static final String BCRYPT_SALT = BCrypt.gensalt(); 35 | 36 | private final Gson gson; 37 | private final UserService userService; 38 | private final TokenService tokenService; 39 | 40 | public AuthController(Gson gson, UserService userService, TokenService tokenService) { 41 | super(tokenService); 42 | this.gson = gson; 43 | this.userService = userService; 44 | this.tokenService = tokenService; 45 | } 46 | 47 | public void init() { 48 | createAdminUser(); 49 | 50 | // AUTH FILTER 51 | before(new AuthFilter(AUTH_ENDPOINT_PREFIX, tokenService)); 52 | 53 | // REGISTRATION ENDPOINT 54 | post(AUTH_ENDPOINT_PREFIX + "/registration", (request, response) -> register(request, response)); 55 | 56 | // LOGIN ENDPOINT 57 | post(AUTH_ENDPOINT_PREFIX + "/login", (request, response) -> login(request, response)); 58 | 59 | // LOGOUT ENDPOINT 60 | post(AUTH_ENDPOINT_PREFIX + "/logout", (request, response) -> logout(request)); 61 | 62 | // REFRESH ENDPOINT 63 | post(AUTH_ENDPOINT_PREFIX + "/token", (request, response) -> refresh(request, response)); 64 | 65 | // ME ENDPOINT 66 | get(AUTH_ENDPOINT_PREFIX + "/me", (request, response) -> me(request, response)); 67 | 68 | // ASSIGN ROLE_PROPERTY 69 | post(AUTH_ENDPOINT_PREFIX + "/roles", (request, response) -> assignRole(request)); 70 | 71 | // REVOKE ROLE_PROPERTY 72 | Spark.delete(AUTH_ENDPOINT_PREFIX + "/roles", (request, response) -> revokeRole(request)); 73 | 74 | } 75 | 76 | private String revokeRole(Request request) throws IOException { 77 | if (hasRole(request, new Role[]{Role.ADMIN})) { 78 | String json = request.raw().getReader().lines().collect(Collectors.joining()); 79 | JsonObject jsonRequest = this.gson.fromJson(json, JsonObject.class); 80 | if (jsonRequest.has(USER_NAME_PROPERTY) && jsonRequest.has(ROLE_PROPERTY)) { 81 | Role role = Role.valueOf(jsonRequest.get(ROLE_PROPERTY).getAsString()); 82 | if (role != null) { 83 | User user = this.userService.get(jsonRequest.get(USER_NAME_PROPERTY).getAsString()); 84 | if (user != null) { 85 | user.revokeRole(role); 86 | this.userService.update(user); 87 | } 88 | } 89 | } 90 | } else { 91 | halt(401); 92 | } 93 | 94 | return ""; 95 | } 96 | 97 | private String assignRole(Request request) throws IOException { 98 | if (hasRole(request, new Role[]{Role.ADMIN})) { 99 | String json = request.raw().getReader().lines().collect(Collectors.joining()); 100 | JsonObject jsonRequest = gson.fromJson(json, JsonObject.class); 101 | if (jsonRequest.has(USER_NAME_PROPERTY) && jsonRequest.has(ROLE_PROPERTY)) { 102 | Role role = Role.valueOf(jsonRequest.get(ROLE_PROPERTY).getAsString()); 103 | if (role != null) { 104 | User user = userService.get(jsonRequest.get(USER_NAME_PROPERTY).getAsString()); 105 | if (user != null) { 106 | user.assignRole(role); 107 | userService.update(user); 108 | } 109 | } 110 | } 111 | } else { 112 | halt(401); 113 | } 114 | 115 | return ""; 116 | } 117 | 118 | private String me(Request request, Response response) { 119 | response.type("application/json"); 120 | String userName = getUserNameFromToken(request); 121 | User user = userService.get(userName); 122 | JsonObject userJson = new JsonObject(); 123 | userJson.addProperty(USER_NAME_PROPERTY, user.getUsername()); 124 | userJson.addProperty(FIRST_NAME_PROPERTY, user.getFirstName()); 125 | userJson.addProperty(LAST_NAME_PROPERTY, user.getLastName()); 126 | return userJson.toString(); 127 | } 128 | 129 | private String refresh(Request request, Response response) { 130 | String authorizationHeader = request.headers(AUTHORIZATION_HEADER); 131 | String token = authorizationHeader.replace(TOKEN_PREFIX, ""); 132 | String userName = getUserNameFromToken(request); 133 | tokenService.revokeToken(token); 134 | String refreshedToken = tokenService.newToken(userService.get(userName)); 135 | response.header(AUTHORIZATION_HEADER, TOKEN_PREFIX + " " + refreshedToken); 136 | return ""; 137 | } 138 | 139 | private String logout(Request request) { 140 | String authorizationHeader = request.headers(AUTHORIZATION_HEADER); 141 | tokenService.revokeToken(authorizationHeader.replace(TOKEN_PREFIX, "")); 142 | return ""; 143 | } 144 | 145 | private String login(Request request, Response response) throws IOException { 146 | String json = request.raw().getReader().lines().collect(Collectors.joining()); 147 | JsonObject jsonRequest = gson.fromJson(json, JsonObject.class); 148 | if (validatePost(jsonRequest)) { 149 | try { 150 | String encryptedPassword = BCrypt.hashpw(jsonRequest.get(PASSWORD_PROPERTY).getAsString(), BCRYPT_SALT); 151 | User user = userService.get(jsonRequest.get(USER_NAME_PROPERTY).getAsString()); 152 | if (user.getPassword().equals(encryptedPassword)) { 153 | response.header(AUTHORIZATION_HEADER, TOKEN_PREFIX + " " + tokenService.newToken(user)); 154 | } 155 | } catch (Exception e) { 156 | response.status(401); 157 | } 158 | } 159 | return ""; 160 | } 161 | 162 | private String register(Request request, Response response) throws IOException { 163 | String json = request.raw().getReader().lines().collect(Collectors.joining()); 164 | JsonObject jsonRequest = gson.fromJson(json, JsonObject.class); 165 | try { 166 | if (validatePost(jsonRequest)) { 167 | userService.register(jsonRequest.get(USER_NAME_PROPERTY).getAsString(), 168 | BCrypt.hashpw(jsonRequest.get(PASSWORD_PROPERTY).getAsString(), BCRYPT_SALT), 169 | jsonRequest.has(FIRST_NAME_PROPERTY) ? jsonRequest.get(FIRST_NAME_PROPERTY).getAsString() : null, 170 | jsonRequest.has(LAST_NAME_PROPERTY) ? jsonRequest.get(LAST_NAME_PROPERTY).getAsString() : null); 171 | return ""; 172 | } else { 173 | response.status(400); 174 | } 175 | } catch (IllegalArgumentException e) { 176 | response.status(400); 177 | } 178 | return ""; 179 | } 180 | 181 | private void createAdminUser() { 182 | userService.register("admin", BCrypt.hashpw("admin", BCRYPT_SALT), null, null); //ADMIN USER 183 | User admin = userService.get("admin"); 184 | admin.assignRole(Role.ADMIN); 185 | userService.update(admin); 186 | } 187 | 188 | private boolean validatePost(JsonObject jsonRequest) { 189 | return jsonRequest != null && jsonRequest.has(USER_NAME_PROPERTY) && jsonRequest.has(PASSWORD_PROPERTY); 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt.controller; 2 | 3 | import com.r6lab.sparkjava.jwt.TokenService; 4 | import com.r6lab.sparkjava.jwt.user.Role; 5 | 6 | import static spark.Spark.get; 7 | import static spark.Spark.halt; 8 | 9 | public class UserController extends AbstractTokenController { 10 | 11 | public UserController(TokenService tokenService) { 12 | super(tokenService); 13 | } 14 | 15 | public void init() { 16 | // PROTECTED ENDPOINT FOR DEVELOPER ROLE_PROPERTY 17 | get("/protected/developer", (request, response) -> { 18 | if (hasRole(request, new Role[]{Role.DEVELOPER, Role.ADMIN})) { 19 | return "PROTECTED RESOURCE FOR DEVELOPER"; 20 | } else { 21 | halt(401); 22 | return ""; 23 | } 24 | }); 25 | 26 | // PROTECTED ENDPOINT FOR MANAGER ROLE_PROPERTY 27 | get("/protected/manager", (request, response) -> { 28 | if (hasRole(request, new Role[]{Role.MANAGER, Role.ADMIN})) { 29 | return "PROTECTED RESOURCE FOR MANAGER"; 30 | } else { 31 | halt(401); 32 | return ""; 33 | } 34 | }); 35 | 36 | // PROTECTED ENDPOINT FOR ADMIN ROLE_PROPERTY 37 | get("/protected/admin", (request, response) -> { 38 | if (hasRole(request, new Role[]{Role.ADMIN})) { 39 | return "PROTECTED RESOURCE FOR ADMIN"; 40 | } else { 41 | halt(401); 42 | return ""; 43 | } 44 | }); 45 | 46 | // PROTECTED ENDPOINT FOR ALL ROLES 47 | get("/protected/all", (request, response) -> { 48 | if (hasRole(request, new Role[]{})) { 49 | return "PROTECTED RESOURCE FOR ALL ROLES"; 50 | } else { 51 | halt(401); 52 | return ""; 53 | } 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/user/Role.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt.user; 2 | 3 | public enum Role { 4 | ADMIN, MANAGER, DEVELOPER 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/user/User.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt.user; 2 | 3 | import java.util.List; 4 | 5 | public final class User { 6 | 7 | private final String username; 8 | private final String password; 9 | private final String firstName; 10 | private final String lastName; 11 | private final List roles; 12 | 13 | private User(String username, String password, String firstName, String lastName, List roles) { 14 | this.username = username; 15 | this.password = password; 16 | this.firstName = firstName; 17 | this.lastName = lastName; 18 | this.roles = roles; 19 | } 20 | 21 | public String getUsername() { 22 | return username; 23 | } 24 | 25 | public String getPassword() { 26 | return password; 27 | } 28 | 29 | public String getFirstName() { 30 | return firstName; 31 | } 32 | 33 | public String getLastName() { 34 | return lastName; 35 | } 36 | 37 | public List getRoles() { 38 | return roles; 39 | } 40 | 41 | public void assignRole(Role role) { 42 | if (!roles.contains(role)) { 43 | roles.add(role); 44 | } 45 | } 46 | 47 | public void revokeRole(Role role) { 48 | if (!roles.contains(role)) { 49 | roles.remove(role); 50 | } 51 | } 52 | 53 | public static final User of(String username, String password, String firstName, String lastName, List roles) { 54 | return new User(username, password, firstName, lastName, roles); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/user/UserPrincipal.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt.user; 2 | 3 | import java.util.List; 4 | 5 | public final class UserPrincipal { 6 | private final String userName; 7 | private final List roles; 8 | 9 | private UserPrincipal(String userName, List roles) { 10 | this.userName = userName; 11 | this.roles = roles; 12 | } 13 | 14 | public String getUserName() { 15 | return userName; 16 | } 17 | 18 | public List getRoles() { 19 | return roles; 20 | } 21 | 22 | public static UserPrincipal of(String userName, List roles) { 23 | return new UserPrincipal(userName, roles); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/r6lab/sparkjava/jwt/user/UserService.java: -------------------------------------------------------------------------------- 1 | package com.r6lab.sparkjava.jwt.user; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.CopyOnWriteArrayList; 6 | 7 | public final class UserService { 8 | 9 | private final List users = new CopyOnWriteArrayList<>(); 10 | 11 | public final void register(String userName, String password, String firstName, String lastName) { 12 | if (users.stream().filter(user -> user.getUsername().equals(userName)).findAny().isPresent()) { 13 | throw new IllegalArgumentException("User already exists"); 14 | } 15 | users.add(User.of(userName, password, firstName, lastName, new ArrayList<>())); 16 | } 17 | 18 | public final User get(String userName) { 19 | return users 20 | .stream() 21 | .filter(user -> user.getUsername().equals(userName)) 22 | .findAny() 23 | .orElseThrow(() -> new IllegalStateException("User does not exist")); 24 | } 25 | 26 | public final void update(User user) { 27 | users.add(user); 28 | } 29 | 30 | } 31 | --------------------------------------------------------------------------------