diff --git a/chatto/src/main/java/org/ros/chatto/BeanConfigurations.java b/chatto/src/main/java/org/ros/chatto/BeanConfigurations.java index fab183d..deed945 100644 --- a/chatto/src/main/java/org/ros/chatto/BeanConfigurations.java +++ b/chatto/src/main/java/org/ros/chatto/BeanConfigurations.java @@ -1,5 +1,7 @@ package org.ros.chatto; +import java.security.SecureRandom; + import org.modelmapper.ModelMapper; import org.ros.chatto.security.AuthenticationSuccessHandlerImpl; import org.springframework.beans.factory.annotation.Value; @@ -8,6 +10,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.security.core.token.KeyBasedPersistenceTokenService; +import org.springframework.security.core.token.TokenService; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; @PropertySource(value = "classpath:queries.properties") @@ -22,7 +26,6 @@ public class BeanConfigurations { @Value("${spring.datasource.password}") private String password; - @Bean public AuthenticationSuccessHandler authenticationSuccessHandler() { return new AuthenticationSuccessHandlerImpl(); @@ -33,17 +36,27 @@ public class BeanConfigurations { ModelMapper modelMapper = new ModelMapper(); return modelMapper; } - + @Bean public MessageSource messageSource() { - final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); - messageSource.setBasenames("classpath:/messages,file:./config/messages"); - messageSource.setUseCodeAsDefaultMessage(true); - messageSource.setDefaultEncoding("UTF-8"); - messageSource.setCacheSeconds(5); - return messageSource; + final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + messageSource.setBasenames("classpath:/messages,file:./config/messages"); + messageSource.setUseCodeAsDefaultMessage(true); + messageSource.setDefaultEncoding("UTF-8"); + messageSource.setCacheSeconds(5); + return messageSource; } - + + @Bean + public TokenService tokenService() { + KeyBasedPersistenceTokenService keyBasedPersistenceTokenService = new KeyBasedPersistenceTokenService(); + keyBasedPersistenceTokenService.setPseudoRandomNumberBytes(10); + keyBasedPersistenceTokenService.setSecureRandom(new SecureRandom()); + keyBasedPersistenceTokenService.setServerInteger(1); + keyBasedPersistenceTokenService.setServerSecret(":"); + return keyBasedPersistenceTokenService; + } + // @Bean // public Connection connection() throws SQLException // { diff --git a/chatto/src/main/java/org/ros/chatto/WebSecurityConfiguration.java b/chatto/src/main/java/org/ros/chatto/WebSecurityConfiguration.java index 6f027b4..67300fc 100644 --- a/chatto/src/main/java/org/ros/chatto/WebSecurityConfiguration.java +++ b/chatto/src/main/java/org/ros/chatto/WebSecurityConfiguration.java @@ -2,22 +2,24 @@ package org.ros.chatto; import org.ros.chatto.logged.MyLogoutSuccessHandler; import org.ros.chatto.logged.MySimpleUrlAuthenticationSuccessHandler; +import org.ros.chatto.security.CustomBasicAuthenticationFilter; import org.ros.chatto.security.MyUserDetailsService; +import org.ros.chatto.security.TokenAuthenticationFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 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.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.UserCache; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; -import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; @Configuration @EnableWebSecurity @@ -29,15 +31,17 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { private MyUserDetailsService myUserDetailsService; @Autowired private PasswordEncoder passwordEncoder; - @Autowired - private UserCache userCache; -// @SuppressWarnings("deprecation") + @Override + @Bean + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(myUserDetailsService); - provider.setUserCache(userCache); provider.setPasswordEncoder(passwordEncoder); return provider; } @@ -52,6 +56,12 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { public static class ApiWebSecurity extends WebSecurityConfigurerAdapter { @Autowired private RESTAuthenticationEntryPoint authenticationEntryPoint; + + @Autowired + private CustomBasicAuthenticationFilter customBasicAuthFilter; + + @Autowired + private TokenAuthenticationFilter tokenFilter; @Override protected void configure(HttpSecurity http) throws Exception { @@ -76,6 +86,14 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { // .and() // .logout(); ; + + http.addFilterBefore(tokenFilter, BasicAuthenticationFilter.class); + + // Creating token when basic authentication is successful and the same token can + // be used to authenticate for further requests +// final CustomBasicAuthenticationFilter customBasicAuthFilter = new CustomBasicAuthenticationFilter( +// authenticationManagerBean()); + http.addFilter(customBasicAuthFilter); } @@ -143,7 +161,6 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { // .clearAuthentication(true) // .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) // .logoutSuccessUrl("/").permitAll(); - } // @Override // protected void configure(AuthenticationManagerBuilder auth) throws Exception { diff --git a/chatto/src/main/java/org/ros/chatto/config/CacheConfig.java b/chatto/src/main/java/org/ros/chatto/config/CacheConfig.java deleted file mode 100644 index 33f8809..0000000 --- a/chatto/src/main/java/org/ros/chatto/config/CacheConfig.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.ros.chatto.config; - - -import org.ros.chatto.security.MyUserDetailsService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.core.userdetails.UserCache; -import org.springframework.security.core.userdetails.cache.SpringCacheBasedUserCache; - -@EnableCaching -@Configuration -public class CacheConfig { - - @Autowired - private CacheManager cacheManager; - - - @Bean - public UserCache userCache() throws Exception { -// return new EhCacheBasedUserCache(); -// Cache cache = (Cache) cacheManager().getCache("userCache"); - Cache cache = cacheManager.getCache("chatUser"); - return new SpringCacheBasedUserCache(cache); - } - -// private net.sf.ehcache.CacheManager cacheManager; - -// @PreDestroy -// public void destroy() { -// cacheManager.shutdown(); -// } -// -// @Bean -// public CacheManager cacheManager() { -//// log.debug("Starting Ehcache"); -// cacheManager = net.sf.ehcache.CacheManager.create(); -// cacheManager.getConfiguration().setMaxBytesLocalHeap("16M"); -// EhCacheCacheManager ehCacheManager = new EhCacheCacheManager(); -// ehCacheManager.setCacheManager(cacheManager); -// return ehCacheManager; -// } -} \ No newline at end of file diff --git a/chatto/src/main/java/org/ros/chatto/config/EhCacheConfig.java b/chatto/src/main/java/org/ros/chatto/config/EhCacheConfig.java new file mode 100644 index 0000000..6e53976 --- /dev/null +++ b/chatto/src/main/java/org/ros/chatto/config/EhCacheConfig.java @@ -0,0 +1,10 @@ +package org.ros.chatto.config; + + +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Configuration; + +@EnableCaching +@Configuration +public class EhCacheConfig { +} \ No newline at end of file diff --git a/chatto/src/main/java/org/ros/chatto/model/UserToken.java b/chatto/src/main/java/org/ros/chatto/model/UserToken.java new file mode 100644 index 0000000..6da2e14 --- /dev/null +++ b/chatto/src/main/java/org/ros/chatto/model/UserToken.java @@ -0,0 +1,31 @@ +package org.ros.chatto.model; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +import lombok.Data; + +@Data +@Entity +@Table(name="tokens") +public class UserToken implements Serializable { + /** + * + */ + private static final long serialVersionUID = -201675581183933341L; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "token_id") + private long tokenID; + private String userName; + private String tokenContent; + private String role; +} diff --git a/chatto/src/main/java/org/ros/chatto/repository/TokenRepository.java b/chatto/src/main/java/org/ros/chatto/repository/TokenRepository.java new file mode 100644 index 0000000..572e61a --- /dev/null +++ b/chatto/src/main/java/org/ros/chatto/repository/TokenRepository.java @@ -0,0 +1,19 @@ +package org.ros.chatto.repository; + +import org.ros.chatto.model.UserToken; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +@Repository +@CacheConfig(cacheNames = "userTokenCache") +public interface TokenRepository extends JpaRepository { + @Cacheable(value = "userTokenCache", key = "#token") + @Query("select t from UserToken t where t.tokenContent = ?1") + public UserToken findByToken(String token); + @Cacheable(value = "userTokenCache", key = "#userName") + @Query("select t from UserToken t where t.userName = ?1") + public UserToken findByUserName(String userName); +} diff --git a/chatto/src/main/java/org/ros/chatto/security/CustomBasicAuthenticationFilter.java b/chatto/src/main/java/org/ros/chatto/security/CustomBasicAuthenticationFilter.java new file mode 100644 index 0000000..92bd61f --- /dev/null +++ b/chatto/src/main/java/org/ros/chatto/security/CustomBasicAuthenticationFilter.java @@ -0,0 +1,66 @@ +package org.ros.chatto.security; + +import org.ros.chatto.model.UserToken; +import org.ros.chatto.repository.UserRepository; +import org.ros.chatto.service.UserService; +import org.ros.chatto.service.UserTokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.token.Token; +import org.springframework.security.core.token.TokenService; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.stereotype.Component; + +@Component +public class CustomBasicAuthenticationFilter extends BasicAuthenticationFilter { + + @Autowired + private UserService userService; + @Autowired + private TokenService tokenService; + @Autowired + private UserTokenService userTokenService; + @Autowired + private UserRepository userRepository; + + @Autowired + public CustomBasicAuthenticationFilter(final AuthenticationManager authenticationManager) { + super(authenticationManager); + } + + @Override + protected void onSuccessfulAuthentication(final javax.servlet.http.HttpServletRequest request, + final javax.servlet.http.HttpServletResponse response, final Authentication authResult) { + // Generate Token + // Save the token for the logged in user + // send token in the response +// String tokenString = UUID.randomUUID().toString(); + +// System.out.println("Role = " + authResult.getAuthorities().iterator().next().getAuthority()); + + UserToken userToken = userTokenService.getToken(authResult.getName()); + Token token; + if (userToken == null) { + token = tokenService.allocateToken(""); + userToken = new UserToken(); + System.out.println("srwrrrrrrrrrrrr = " + authResult.getName()); +// ChatUser user = userService.findByUserName(authResult.getName()); +// ChatUser user = userRepository.findByUserName("hmm"); + userToken.setTokenContent(token.getKey()); +// userToken.setTokenContent(tokenString); + userToken.setUserName(authResult.getName()); + userToken.setRole(authResult.getAuthorities().iterator().next().getAuthority()); + userTokenService.saveToken(userToken); + response.setHeader("X-AUTH-TOKEN", token.getKey()); + } + else { + token = tokenService.verifyToken(userToken.getTokenContent()); + if(token!=null) { + response.setHeader("X-AUTH-TOKEN", token.getKey()); + } + } + + } + +} \ No newline at end of file diff --git a/chatto/src/main/java/org/ros/chatto/security/MyUserDetailsService.java b/chatto/src/main/java/org/ros/chatto/security/MyUserDetailsService.java index 9b5a0fc..386d782 100644 --- a/chatto/src/main/java/org/ros/chatto/security/MyUserDetailsService.java +++ b/chatto/src/main/java/org/ros/chatto/security/MyUserDetailsService.java @@ -72,14 +72,6 @@ public class MyUserDetailsService implements UserDetailsService { // } System.out.println("Test from userdetails"); - ValueWrapper valueWrapper = cacheManager.getCache("chatUser").get("hmm"); - if (valueWrapper != null) { - UserDetails userDetails = (UserDetails) valueWrapper.get(); - if (userDetails != null) { - System.out.println("cache username = " + userDetails.getUsername()); - System.out.println("cache password = " + userDetails.getPassword()); - } - } if (userRoles.size() == 0) { throw new UsernameNotFoundException(username); } diff --git a/chatto/src/main/java/org/ros/chatto/security/TokenAuthenticationFilter.java b/chatto/src/main/java/org/ros/chatto/security/TokenAuthenticationFilter.java new file mode 100644 index 0000000..6c5b4f3 --- /dev/null +++ b/chatto/src/main/java/org/ros/chatto/security/TokenAuthenticationFilter.java @@ -0,0 +1,88 @@ +package org.ros.chatto.security; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import org.ros.chatto.model.UserToken; +import org.ros.chatto.repository.TokenRepository; +import org.ros.chatto.repository.UserRoleRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.token.Token; +import org.springframework.security.core.token.TokenService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.GenericFilterBean; + + +@Component +public class TokenAuthenticationFilter extends GenericFilterBean { + + @Autowired + UserRoleRepository userRoleRepository; + + @Autowired + TokenRepository tokenRepository; + + @Autowired + TokenService tokenService; + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) + throws IOException, ServletException { + final HttpServletRequest httpRequest = (HttpServletRequest) request; + + // extract token from header + final String accessToken = httpRequest.getHeader("X-AUTH-TOKEN"); + if (null != accessToken) { + // get and check whether token is valid ( from DB or file wherever you are + // storing the token) + UserToken userToken = tokenRepository.findByToken(accessToken); + + if (userToken == null) { + throw new UsernameNotFoundException("Token not associated with any user"); + } + Token token = tokenService.verifyToken(userToken.getTokenContent()); + + if (token == null) { + throw new UsernameNotFoundException("Token not issued by us"); + } + + String userName = userToken.getUserName(); + if (userName == null) { + throw new UsernameNotFoundException("User not found"); + } + +// List userRoles = userRoleRepository.findByUser(chatUser.getUserName()); +// // Populate SecurityContextHolder by fetching relevant information using token +// final UserDetails userPrincipal = User.withUsername(chatUser.getUserName()).password(chatUser.getPassword()) +// .roles(userRoles.stream().map(userRole -> { +//// System.out.println("role = " + userRole.getRole().getName()); +// return userRole.getRole().getName(); +// }).toArray(size -> new String[size])).build(); +// final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( +// userPrincipal, null, userPrincipal.getAuthorities()); + SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(userToken.getRole()); + List updatedAuthorities = new ArrayList(); + updatedAuthorities.add(simpleGrantedAuthority); + + + + final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userName, token.getKey(), updatedAuthorities); + SecurityContextHolder.getContext().setAuthentication(authentication); + + } + + chain.doFilter(request, response); + } + +} \ No newline at end of file diff --git a/chatto/src/main/java/org/ros/chatto/service/UserTokenService.java b/chatto/src/main/java/org/ros/chatto/service/UserTokenService.java new file mode 100644 index 0000000..de584c5 --- /dev/null +++ b/chatto/src/main/java/org/ros/chatto/service/UserTokenService.java @@ -0,0 +1,33 @@ +package org.ros.chatto.service; + +import javax.transaction.Transactional; + +import org.ros.chatto.model.UserToken; +import org.ros.chatto.repository.TokenRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service + +public class UserTokenService { + @Autowired + private TokenRepository tokenRepository; + + +// @Cacheable + public UserToken getToken(String userName) + { + return tokenRepository.findByUserName(userName); + } + + @Transactional + public void saveToken(UserToken userToken) + { + UserToken userToken2 = tokenRepository.findByToken(userToken.getTokenContent()); + if(userToken2!=null) { + System.out.println("Found valid token"); + return; + } + tokenRepository.save(userToken); + } +} diff --git a/chatto/src/main/resources/ehcache.xml b/chatto/src/main/resources/ehcache.xml index 8cbdabe..227d172 100644 --- a/chatto/src/main/resources/ehcache.xml +++ b/chatto/src/main/resources/ehcache.xml @@ -6,9 +6,9 @@ - + java.lang.String - org.springframework.security.core.userdetails.UserDetails + org.ros.chatto.model.UserToken 600