Add login endpoint
parent
5c3a592a55
commit
18684754ac
|
@ -1,5 +1,6 @@
|
||||||
package ba.steleks.error;
|
package ba.steleks.error;
|
||||||
|
|
||||||
|
import ba.steleks.error.exception.CustomHttpStatusException;
|
||||||
import ba.steleks.error.exception.ExternalServiceException;
|
import ba.steleks.error.exception.ExternalServiceException;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
@ -44,6 +45,17 @@ public class RestErrorHandler {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(CustomHttpStatusException.class)
|
||||||
|
@ResponseBody
|
||||||
|
public ResponseEntity<Map<String, String>> handleAllCustomExceptions(CustomHttpStatusException ex) {
|
||||||
|
Map<String, String> map= new HashMap<String, String>();
|
||||||
|
map.put("status", ex.getStatusCode().toString());
|
||||||
|
map.put("error", ex.getMessage());
|
||||||
|
|
||||||
|
return ResponseEntity.status(ex.getStatusCode()).body(map);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ExceptionHandler(HttpStatusCodeException.class)
|
@ExceptionHandler(HttpStatusCodeException.class)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResponseEntity<Map<String, String>> handleAllExceptions(HttpStatusCodeException ex) {
|
public ResponseEntity<Map<String, String>> handleAllExceptions(HttpStatusCodeException ex) {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package ba.steleks.error.exception;/**
|
||||||
|
* Created by ensar on 30/05/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.client.HttpStatusCodeException;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class CustomHttpStatusException extends HttpStatusCodeException {
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public CustomHttpStatusException(HttpStatus statusCode, String message) {
|
||||||
|
super(statusCode);
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomHttpStatusException(HttpStatus statusCode, String statusText, String message) {
|
||||||
|
super(statusCode, statusText);
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomHttpStatusException(HttpStatus statusCode, String statusText, byte[] responseBody, Charset responseCharset, String message) {
|
||||||
|
super(statusCode, statusText, responseBody, responseCharset);
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomHttpStatusException(HttpStatus statusCode, String statusText, HttpHeaders responseHeaders, byte[] responseBody, Charset responseCharset, String message) {
|
||||||
|
super(statusCode, statusText, responseHeaders, responseBody, responseCharset);
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package ba.steleks.storage.store;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ensar on 30/05/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class HashMapKeyValueStore<K, V> implements KeyValueStore<K, V> {
|
||||||
|
|
||||||
|
private final Map<K, V> kvMap;
|
||||||
|
|
||||||
|
public HashMapKeyValueStore() {
|
||||||
|
kvMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMapKeyValueStore(Map<K, V> kvMap) {
|
||||||
|
this.kvMap = kvMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(K key, V value) {
|
||||||
|
kvMap.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(K key) {
|
||||||
|
return kvMap.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(K key) {
|
||||||
|
return kvMap.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(K key) {
|
||||||
|
kvMap.remove(key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ba.steleks.storage.store;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ensar on 30/05/17.
|
||||||
|
*/
|
||||||
|
public interface KeyValueStore<K, V> {
|
||||||
|
void save(K key, V value);
|
||||||
|
V get(K key);
|
||||||
|
boolean contains(K key);
|
||||||
|
void remove(K key);
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package ba.steleks.util;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ensar on 30/05/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class CalendarUtils {
|
||||||
|
public static Calendar getUTCCalendar() {
|
||||||
|
return Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,23 @@
|
||||||
package ba.steleks.controller;
|
package ba.steleks.controller;
|
||||||
|
|
||||||
|
import ba.steleks.error.exception.CustomHttpStatusException;
|
||||||
import ba.steleks.model.AuthRequest;
|
import ba.steleks.model.AuthRequest;
|
||||||
|
import ba.steleks.model.User;
|
||||||
import ba.steleks.repository.UsersJpaRepository;
|
import ba.steleks.repository.UsersJpaRepository;
|
||||||
|
import ba.steleks.security.SessionIdentifierGenerator;
|
||||||
|
import ba.steleks.security.token.TokenStore;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by admin on 13/05/2017.
|
* Created by admin on 13/05/2017.
|
||||||
*/
|
*/
|
||||||
|
@ -17,16 +26,40 @@ public class AuthenticationController {
|
||||||
|
|
||||||
private UsersJpaRepository usersJpaRepository;
|
private UsersJpaRepository usersJpaRepository;
|
||||||
private PasswordEncoder passwordEncoder;
|
private PasswordEncoder passwordEncoder;
|
||||||
|
private TokenStore tokenStore;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AuthenticationController(UsersJpaRepository usersJpaRepository, PasswordEncoder passwordEncoder) {
|
public AuthenticationController(UsersJpaRepository usersJpaRepository, PasswordEncoder passwordEncoder, TokenStore tokenStore) {
|
||||||
this.passwordEncoder = passwordEncoder;
|
|
||||||
this.usersJpaRepository = usersJpaRepository;
|
this.usersJpaRepository = usersJpaRepository;
|
||||||
|
this.passwordEncoder = passwordEncoder;
|
||||||
|
this.tokenStore = tokenStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(path = "/accesstoken", method = RequestMethod.POST)
|
@RequestMapping(path = "/accesstoken", method = RequestMethod.POST)
|
||||||
public String generateToken(@RequestBody AuthRequest body){
|
public ResponseEntity<?> generateToken(@RequestBody AuthRequest body) {
|
||||||
return passwordEncoder.matches(body.getPassword(), usersJpaRepository.findByUsername(body.getUsername()).getPasswordHash()) ? "true" : "false";
|
if(body.getUsername() == null || body.getPassword() == null) {
|
||||||
|
throw new CustomHttpStatusException(HttpStatus.BAD_REQUEST,
|
||||||
|
"'username' and 'password' fields are mandatory!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
User user = usersJpaRepository.findByUsername(body.getUsername());
|
||||||
|
if(user == null) {
|
||||||
|
throw new CustomHttpStatusException(HttpStatus.NOT_FOUND,
|
||||||
|
"User with username " + body.getUsername() + " not found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passwordEncoder.matches(body.getPassword(), user.getPasswordHash())) {
|
||||||
|
String token = new SessionIdentifierGenerator().nextSessionId();
|
||||||
|
tokenStore.saveToken(user.getId(), token);
|
||||||
|
Map<String, String> response = new HashMap<>();
|
||||||
|
response.put("token", token);
|
||||||
|
response.put("userId", String.valueOf(user.getId()));
|
||||||
|
return ResponseEntity
|
||||||
|
.ok()
|
||||||
|
.body(response);
|
||||||
|
} else {
|
||||||
|
throw new CustomHttpStatusException(HttpStatus.UNAUTHORIZED,
|
||||||
|
"Invalid password!");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package ba.steleks.model;/**
|
||||||
* Created by ensar on 22/03/17.
|
* Created by ensar on 22/03/17.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ba.steleks.security.UserPasswordEntityListener;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
@ -12,9 +13,8 @@ import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@EntityListeners(UserPasswordEntityListener.class)
|
||||||
public class User {
|
public class User {
|
||||||
private static final Logger logger =
|
|
||||||
Logger.getLogger(User.class.getName());
|
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
@ -35,6 +35,10 @@ public class User {
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
||||||
private String passwordHash;
|
private String passwordHash;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private String password;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
|
@ -48,7 +52,7 @@ public class User {
|
||||||
@JoinColumn
|
@JoinColumn
|
||||||
private Set<MembershipType> membershipTypes;
|
private Set<MembershipType> membershipTypes;
|
||||||
|
|
||||||
@ManyToMany
|
@ManyToMany(fetch = FetchType.EAGER)
|
||||||
@JoinColumn
|
@JoinColumn
|
||||||
private Set<UserRole> userRoles;
|
private Set<UserRole> userRoles;
|
||||||
|
|
||||||
|
@ -156,6 +160,14 @@ public class User {
|
||||||
this.userRoles = userRoles;
|
this.userRoles = userRoles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
@PrePersist
|
@PrePersist
|
||||||
protected void onCreate() {
|
protected void onCreate() {
|
||||||
this.registrationDate = new Timestamp(new Date().getTime());
|
this.registrationDate = new Timestamp(new Date().getTime());
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package ba.steleks.security;/**
|
||||||
|
* Created by ensar on 30/05/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
|
||||||
|
public class CustomUrlUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
|
||||||
|
|
||||||
|
public CustomUrlUsernamePasswordAuthenticationFilter() {
|
||||||
|
super();
|
||||||
|
setRequiresAuthenticationRequestMatcher(
|
||||||
|
new AntPathRequestMatcher("/accesstoken", "POST")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import javax.servlet.FilterChain;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletResponseWrapper;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -38,5 +39,6 @@ public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
|
||||||
Set<UserRole> userRoles = UserRoleFactory.fromGrantedAuthorities(authResult.getAuthorities());
|
Set<UserRole> userRoles = UserRoleFactory.fromGrantedAuthorities(authResult.getAuthorities());
|
||||||
|
|
||||||
TokenAuthenticationService.addAuthenticationHeader(response, authResult.getName(), userRoles);
|
TokenAuthenticationService.addAuthenticationHeader(response, authResult.getName(), userRoles);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
|
@ -35,10 +34,12 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
http.csrf().disable().authorizeRequests()
|
http.csrf().disable().authorizeRequests()
|
||||||
|
.antMatchers("/accesstoken").permitAll()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
.and()
|
.and()
|
||||||
.addFilterBefore(new JWTLoginFilter("/accesstoken", authenticationManager()),
|
// .addFilterBefore(new JWTLoginFilter("/accesstoken", authenticationManager()),
|
||||||
UsernamePasswordAuthenticationFilter.class)
|
// CustomUrlUsernamePasswordAuthenticationFilter.class)
|
||||||
.addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
|
.addFilterBefore(new JWTAuthenticationFilter(), CustomUrlUsernamePasswordAuthenticationFilter.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package ba.steleks.security;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ensar on 30/05/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
public final class SessionIdentifierGenerator {
|
||||||
|
private SecureRandom random = new SecureRandom();
|
||||||
|
|
||||||
|
public String nextSessionId() {
|
||||||
|
return new BigInteger(130, random).toString(32);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,20 +3,14 @@ package ba.steleks.security;/**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ba.steleks.model.User;
|
import ba.steleks.model.User;
|
||||||
import ba.steleks.model.UserRole;
|
|
||||||
import ba.steleks.repository.UsersJpaRepository;
|
import ba.steleks.repository.UsersJpaRepository;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class SteleksUsersDetailsService implements UserDetailsService {
|
public class SteleksUsersDetailsService implements UserDetailsService {
|
||||||
|
|
||||||
|
|
|
@ -10,15 +10,14 @@ import org.springframework.security.core.Authentication;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.sql.Date;
|
import java.sql.Date;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by ensar on 28/05/17.
|
* Created by ensar on 28/05/17.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class TokenAuthenticationService {
|
public class TokenAuthenticationService {
|
||||||
|
|
||||||
|
|
||||||
static final long EXPIRATION_TIME = 864_000_000; // 10 days
|
static final long EXPIRATION_TIME = 864_000_000; // 10 days
|
||||||
static final String SECRET = "ASteleksSecret";
|
static final String SECRET = "ASteleksSecret";
|
||||||
|
@ -26,16 +25,17 @@ class TokenAuthenticationService {
|
||||||
static final String HEADER_STRING = "Authorization";
|
static final String HEADER_STRING = "Authorization";
|
||||||
static final String ROLES = "roles";
|
static final String ROLES = "roles";
|
||||||
|
|
||||||
static void addAuthenticationHeader(HttpServletResponse res, String username, Set<UserRole> userRoleSet) {
|
public static void addAuthenticationHeader(HttpServletResponse res, String username, Set<UserRole> userRoleSet) {
|
||||||
String JWT = Jwts.builder()
|
String JWT = Jwts.builder()
|
||||||
.setSubject(username)
|
.setSubject(username)
|
||||||
.claim(ROLES, UserRoleFactory.toString(userRoleSet))
|
.claim(ROLES, UserRoleFactory.toString(userRoleSet))
|
||||||
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
|
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
|
||||||
.signWith(SignatureAlgorithm.HS512, SECRET).compact();
|
.signWith(SignatureAlgorithm.HS512, SECRET).compact();
|
||||||
|
|
||||||
res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + JWT);
|
res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + JWT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Authentication getAuthentication(HttpServletRequest request) {
|
public static Authentication getAuthentication(HttpServletRequest request) {
|
||||||
String token = request.getHeader(HEADER_STRING);
|
String token = request.getHeader(HEADER_STRING);
|
||||||
|
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package ba.steleks.security;
|
||||||
|
|
||||||
|
import ba.steleks.model.User;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
import javax.persistence.PrePersist;
|
||||||
|
import javax.persistence.PreUpdate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ensar on 30/05/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class UserPasswordEntityListener {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
@PreUpdate
|
||||||
|
public void onUserUpdate(User user) {
|
||||||
|
if(user.getPassword() != null) {
|
||||||
|
user.setPasswordHash(passwordEncoder.encode(user.getPassword()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package ba.steleks.security.token;
|
||||||
|
|
||||||
|
import ba.steleks.storage.store.KeyValueStore;
|
||||||
|
import ba.steleks.util.CalendarUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ensar on 30/05/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class BasicTokenStore implements TokenStore {
|
||||||
|
|
||||||
|
// Default one hour ttl
|
||||||
|
public static final long DEFAULT_TTL =
|
||||||
|
TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS);
|
||||||
|
|
||||||
|
private KeyValueStore<Long, BasicToken> tokenStore;
|
||||||
|
private long ttl = DEFAULT_TTL;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public BasicTokenStore(KeyValueStore<Long, BasicToken> tokenStore) {
|
||||||
|
this.tokenStore = tokenStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidToken(Long id, String token) {
|
||||||
|
if (tokenStore.contains(id)) {
|
||||||
|
// Find token in store
|
||||||
|
BasicToken basicToken = tokenStore.get(id);
|
||||||
|
|
||||||
|
// Token is invalid, there is different token saved in store
|
||||||
|
if (!basicToken.token.equals(token)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token is invalid, it has expired
|
||||||
|
if(basicToken.saveTime + ttl < CalendarUtils.getUTCCalendar().getTimeInMillis()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token valid!
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// No id in store, there is no token
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveToken(Long id, String token) {
|
||||||
|
BasicToken basicToken = new BasicToken();
|
||||||
|
basicToken.token = token;
|
||||||
|
basicToken.saveTime = CalendarUtils.getUTCCalendar().getTimeInMillis();
|
||||||
|
tokenStore.save(id, basicToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeToken(Long id, String token) {
|
||||||
|
tokenStore.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BasicToken {
|
||||||
|
String token;
|
||||||
|
Long saveTime;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package ba.steleks.security.token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ensar on 30/05/17.
|
||||||
|
*/
|
||||||
|
public interface TokenStore {
|
||||||
|
|
||||||
|
boolean isValidToken(Long id, String token);
|
||||||
|
|
||||||
|
void saveToken(Long id, String token);
|
||||||
|
|
||||||
|
void removeToken(Long id, String token);
|
||||||
|
|
||||||
|
}
|
Reference in New Issue