diff --git a/users/src/main/java/ba/steleks/AutowireHelper.java b/users/src/main/java/ba/steleks/AutowireHelper.java new file mode 100644 index 0000000..4432bed --- /dev/null +++ b/users/src/main/java/ba/steleks/AutowireHelper.java @@ -0,0 +1,47 @@ +package ba.steleks; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * Helper class which is able to autowire a specified class. It holds a static reference to the {@link org + * .springframework.context.ApplicationContext}. + */ +public final class AutowireHelper implements ApplicationContextAware { + + private static final AutowireHelper INSTANCE = new AutowireHelper(); + private static ApplicationContext applicationContext; + + private AutowireHelper() { + } + + /** + * Tries to autowire the specified instance of the class if one of the specified beans which need to be autowired + * are null. + * + * @param classToAutowire the instance of the class which holds @Autowire annotations + * @param beansToAutowireInClass the beans which have the @Autowire annotation in the specified {#classToAutowire} + */ + public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) { + for (Object bean : beansToAutowireInClass) { + if (bean == null) { + applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire); + return; + } + } + } + + @Override + public void setApplicationContext(final ApplicationContext applicationContext) { + AutowireHelper.applicationContext = applicationContext; + } + + /** + * @return the singleton instance. + */ + public static AutowireHelper getInstance() { + return INSTANCE; + } + +} + diff --git a/users/src/main/java/ba/steleks/SteleksServiceApplication.java b/users/src/main/java/ba/steleks/SteleksServiceApplication.java index 2f253d4..493a073 100644 --- a/users/src/main/java/ba/steleks/SteleksServiceApplication.java +++ b/users/src/main/java/ba/steleks/SteleksServiceApplication.java @@ -38,18 +38,6 @@ class ServiceInstanceRestController { this.discoveryClient=discoveryClient; } - @Value("${user.password}") - private String password; - - @RequestMapping( - value = "/whoami/{username}", - method = RequestMethod.GET, - produces = MediaType.TEXT_PLAIN_VALUE) - public String whoami(@PathVariable("username") String username) { - return String.format("Hello! You're %s and you'll become Developer, " + - "but only if your password is '%s'!\n", username, password); - } - @RequestMapping("/service-instances/{applicationName}") public List serviceInstancesByApplicationName( @PathVariable String applicationName) { diff --git a/users/src/main/java/ba/steleks/UsersConfig.java b/users/src/main/java/ba/steleks/UsersConfig.java index 707e12f..bd460a2 100644 --- a/users/src/main/java/ba/steleks/UsersConfig.java +++ b/users/src/main/java/ba/steleks/UsersConfig.java @@ -1,7 +1,11 @@ package ba.steleks; +import ba.steleks.security.SteleksUsersDetailsService; +import ba.steleks.security.token.HashTokenEncoder; +import ba.steleks.security.token.TokenEncoder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -11,7 +15,22 @@ import org.springframework.security.crypto.password.PasswordEncoder; @Configuration public class UsersConfig { @Bean - public static PasswordEncoder providePasswordEncoder(){ + public static PasswordEncoder providePasswordEncoder() { return new BCryptPasswordEncoder(); } + + @Bean + public static TokenEncoder provideTokenEncoder() { + return new HashTokenEncoder(); + } + + @Bean + public static AutowireHelper autowireHelper() { + return AutowireHelper.getInstance(); + } + + @Bean + public UserDetailsService provideUserDetailsService() { + return new SteleksUsersDetailsService(); + } } diff --git a/users/src/main/java/ba/steleks/model/User.java b/users/src/main/java/ba/steleks/model/User.java index e008066..0b11508 100644 --- a/users/src/main/java/ba/steleks/model/User.java +++ b/users/src/main/java/ba/steleks/model/User.java @@ -29,6 +29,7 @@ public class User { @NotNull private Timestamp registrationDate; @NotNull + @Column(unique = true) private String email; @NotNull private String contactNumber; @@ -40,6 +41,7 @@ public class User { private String password; @NotNull + @Column(unique = true) private String username; private String profilePictureUrl; diff --git a/users/src/main/java/ba/steleks/security/SecurityConfig.java b/users/src/main/java/ba/steleks/security/SecurityConfig.java index 46c8121..4ceec80 100644 --- a/users/src/main/java/ba/steleks/security/SecurityConfig.java +++ b/users/src/main/java/ba/steleks/security/SecurityConfig.java @@ -1,4 +1,6 @@ -package ba.steleks.security;/** +package ba.steleks.security; + +/** * Created by ensar on 16/05/17. */ @@ -8,6 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -19,20 +22,19 @@ import org.springframework.security.core.userdetails.UserDetailsService; @ComponentScan("org.baeldung.security") public class SecurityConfig extends WebSecurityConfigurerAdapter { - @Bean - public UserDetailsService provideUserDetailsService() { - return new SteleksUsersDetailsService(); + private final UserDetailsService userDetailsService; + + private final TokenStore tokenStore; + + private final UsersJpaRepository usersJpaRepository; + + @Autowired + public SecurityConfig(UserDetailsService userDetailsService, TokenStore tokenStore, UsersJpaRepository usersJpaRepository) { + this.userDetailsService = userDetailsService; + this.tokenStore = tokenStore; + this.usersJpaRepository = usersJpaRepository; } - @Autowired - private UserDetailsService userDetailsService; - - @Autowired - private TokenStore tokenStore; - - @Autowired - private UsersJpaRepository usersJpaRepository; - @Override protected void configure( AuthenticationManagerBuilder auth) throws Exception { @@ -43,6 +45,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { http.csrf().disable().authorizeRequests() .antMatchers("/accesstoken", "/accesstoken/**", "/").permitAll() + .antMatchers(HttpMethod.POST, "/users").permitAll() .anyRequest().authenticated() .and() .addFilterBefore(new AuthenticationFilter(tokenStore, usersJpaRepository), CustomUrlUsernamePasswordAuthenticationFilter.class); diff --git a/users/src/main/java/ba/steleks/security/UserPasswordEntityListener.java b/users/src/main/java/ba/steleks/security/UserPasswordEntityListener.java index 8a059a8..3b80734 100644 --- a/users/src/main/java/ba/steleks/security/UserPasswordEntityListener.java +++ b/users/src/main/java/ba/steleks/security/UserPasswordEntityListener.java @@ -1,8 +1,12 @@ package ba.steleks.security; +import ba.steleks.AutowireHelper; import ba.steleks.model.User; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; +import org.springframework.web.context.support.SpringBeanAutowiringSupport; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; @@ -11,6 +15,7 @@ import javax.persistence.PreUpdate; * Created by ensar on 30/05/17. */ +@Component public class UserPasswordEntityListener { @Autowired @@ -19,6 +24,7 @@ public class UserPasswordEntityListener { @PrePersist @PreUpdate public void onUserUpdate(User user) { + AutowireHelper.autowire(this, passwordEncoder); if(user.getPassword() != null) { user.setPasswordHash(passwordEncoder.encode(user.getPassword())); } diff --git a/users/src/main/java/ba/steleks/security/token/BasicInMemoryTokenStore.java b/users/src/main/java/ba/steleks/security/token/BasicInMemoryTokenStore.java index 18548ba..72d9298 100644 --- a/users/src/main/java/ba/steleks/security/token/BasicInMemoryTokenStore.java +++ b/users/src/main/java/ba/steleks/security/token/BasicInMemoryTokenStore.java @@ -1,12 +1,10 @@ package ba.steleks.security.token; -import ba.steleks.model.UserRole; 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.Set; import java.util.concurrent.TimeUnit; /** @@ -21,17 +19,54 @@ public class BasicInMemoryTokenStore implements TokenStore { TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS); private static KeyValueStore tokenStore; + private TokenEncoder tokenEncoder; private long ttl = DEFAULT_TTL; @Autowired - public BasicInMemoryTokenStore(KeyValueStore tokenStore) { + public BasicInMemoryTokenStore(KeyValueStore tokenStore, TokenEncoder tokenEncoder) { if(BasicInMemoryTokenStore.tokenStore == null) { BasicInMemoryTokenStore.tokenStore = tokenStore; } + this.tokenEncoder = tokenEncoder; } @Override public boolean isValidToken(String token) { + return validateToken(encodeToken(token)); + } + + @Override + public Long getTokenInfo(String token) { + token = encodeToken(token); + System.out.println("token = " + token); + if (validateToken(token)) { + // Find token in store + TokenInfo basicToken = tokenStore.get(token); + + return basicToken.userId; + } else { + // No token in store + return null; + } + } + + @Override + public void saveToken(Long id, String tokenKey) { + tokenKey = encodeToken(tokenKey); + System.out.println("id = [" + id + "], tokenKey = [" + tokenKey + "]"); + TokenInfo tokenInfo = new TokenInfo(); + tokenInfo.userId = id; + tokenInfo.saveTime = CalendarUtils.getUTCCalendar().getTimeInMillis(); + tokenStore.save(tokenKey, tokenInfo); + } + + @Override + public void removeToken(String token) { + token = encodeToken(token); + tokenStore.remove(token); + } + + private boolean validateToken(String token) { if (tokenStore.contains(token)) { // Find token in store TokenInfo basicToken = tokenStore.get(token); @@ -50,30 +85,8 @@ public class BasicInMemoryTokenStore implements TokenStore { } } - @Override - public Long getTokenInfo(String token) { - if (isValidToken(token)) { - // Find token in store - TokenInfo basicToken = tokenStore.get(token); - - return basicToken.userId; - } else { - // No token in store - return null; - } - } - - @Override - public void saveToken(Long id, String token) { - TokenInfo tokenInfo = new TokenInfo(); - tokenInfo.userId = id; - tokenInfo.saveTime = CalendarUtils.getUTCCalendar().getTimeInMillis(); - tokenStore.save(token, tokenInfo); - } - - @Override - public void removeToken(String token) { - tokenStore.remove(token); + private String encodeToken(String token) { + return tokenEncoder.encodeToken(token); } private static class TokenInfo { diff --git a/users/src/main/java/ba/steleks/security/token/HashTokenEncoder.java b/users/src/main/java/ba/steleks/security/token/HashTokenEncoder.java new file mode 100644 index 0000000..9a3a3cc --- /dev/null +++ b/users/src/main/java/ba/steleks/security/token/HashTokenEncoder.java @@ -0,0 +1,11 @@ +package ba.steleks.security.token; + +import org.apache.commons.codec.digest.DigestUtils; + +public class HashTokenEncoder implements TokenEncoder { + + @Override + public String encodeToken(String token) { + return DigestUtils.sha256Hex(token); + } +} diff --git a/users/src/main/java/ba/steleks/security/token/TokenEncoder.java b/users/src/main/java/ba/steleks/security/token/TokenEncoder.java new file mode 100644 index 0000000..cd90951 --- /dev/null +++ b/users/src/main/java/ba/steleks/security/token/TokenEncoder.java @@ -0,0 +1,7 @@ +package ba.steleks.security.token; + +public interface TokenEncoder { + + String encodeToken(String token); + +} diff --git a/users/src/main/java/ba/steleks/security/token/TokenStore.java b/users/src/main/java/ba/steleks/security/token/TokenStore.java index eb9cd8c..8723ced 100644 --- a/users/src/main/java/ba/steleks/security/token/TokenStore.java +++ b/users/src/main/java/ba/steleks/security/token/TokenStore.java @@ -13,7 +13,7 @@ public interface TokenStore { boolean isValidToken(String token); - void saveToken(Long id, String token); + void saveToken(Long id, String tokenKey); void removeToken(String token);