diff --git a/starter_code/pom.xml b/starter_code/pom.xml index 608bb212f..17ba6c997 100644 --- a/starter_code/pom.xml +++ b/starter_code/pom.xml @@ -27,11 +27,17 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-security org.springframework.boot - spring-boot-starter-tomcat + spring-boot-starter-web + + + + com.auth0 + java-jwt + 3.10.3 @@ -44,11 +50,18 @@ spring-boot-starter-test test - - org.codehaus.mojo - tomcat-maven-plugin - 1.1 + org.springframework.security + spring-security-test + test + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.springframework.security + spring-security-config diff --git a/starter_code/src/main/java/com/example/demo/SareetaApplication.java b/starter_code/src/main/java/com/example/demo/SareetaApplication.java index f161f699d..7fdcc332f 100644 --- a/starter_code/src/main/java/com/example/demo/SareetaApplication.java +++ b/starter_code/src/main/java/com/example/demo/SareetaApplication.java @@ -3,13 +3,19 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.context.annotation.Bean; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @EnableJpaRepositories("com.example.demo.model.persistence.repositories") @EntityScan("com.example.demo.model.persistence") -@SpringBootApplication +@SpringBootApplication(exclude = {SecurityAutoConfiguration.class}) public class SareetaApplication { - + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder(){ + return new BCryptPasswordEncoder(); + } public static void main(String[] args) { SpringApplication.run(SareetaApplication.class, args); } diff --git a/starter_code/src/main/java/com/example/demo/controllers/UserController.java b/starter_code/src/main/java/com/example/demo/controllers/UserController.java index 87e8089df..44893c62a 100644 --- a/starter_code/src/main/java/com/example/demo/controllers/UserController.java +++ b/starter_code/src/main/java/com/example/demo/controllers/UserController.java @@ -1,10 +1,9 @@ package com.example.demo.controllers; -import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -28,6 +27,9 @@ public class UserController { @Autowired private CartRepository cartRepository; + @Autowired + private BCryptPasswordEncoder bCryptPasswordEncoder; + @GetMapping("/id/{id}") public ResponseEntity findById(@PathVariable Long id) { return ResponseEntity.of(userRepository.findById(id)); @@ -38,7 +40,7 @@ public ResponseEntity findByUserName(@PathVariable String username) { User user = userRepository.findByUsername(username); return user == null ? ResponseEntity.notFound().build() : ResponseEntity.ok(user); } - + @PostMapping("/create") public ResponseEntity createUser(@RequestBody CreateUserRequest createUserRequest) { User user = new User(); @@ -46,6 +48,11 @@ public ResponseEntity createUser(@RequestBody CreateUserRequest createUser Cart cart = new Cart(); cartRepository.save(cart); user.setCart(cart); + if(createUserRequest.getPassword().length()<7 || + !createUserRequest.getPassword().equals(createUserRequest.getConfirmPassword())){ + return ResponseEntity.badRequest().build(); + } + user.setPassword(bCryptPasswordEncoder.encode(createUserRequest.getPassword())); userRepository.save(user); return ResponseEntity.ok(user); } diff --git a/starter_code/src/main/java/com/example/demo/model/persistence/User.java b/starter_code/src/main/java/com/example/demo/model/persistence/User.java index ab85ccc60..462d51cae 100644 --- a/starter_code/src/main/java/com/example/demo/model/persistence/User.java +++ b/starter_code/src/main/java/com/example/demo/model/persistence/User.java @@ -26,12 +26,23 @@ public class User { @Column(nullable = false, unique = true) @JsonProperty private String username; - @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "cart_id", referencedColumnName = "id") @JsonIgnore private Cart cart; - + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @Column(nullable = false) + private String password; + public String getPassword(){ + return password; + } + public void setPassword(String password) { + this.password = password; + } + + public User() { + } + public Cart getCart() { return cart; } @@ -55,7 +66,6 @@ public String getUsername() { public void setUsername(String username) { this.username = username; } - - - + + } diff --git a/starter_code/src/main/java/com/example/demo/model/requests/CreateUserRequest.java b/starter_code/src/main/java/com/example/demo/model/requests/CreateUserRequest.java index a92d0bbb6..5911f9e41 100644 --- a/starter_code/src/main/java/com/example/demo/model/requests/CreateUserRequest.java +++ b/starter_code/src/main/java/com/example/demo/model/requests/CreateUserRequest.java @@ -7,6 +7,12 @@ public class CreateUserRequest { @JsonProperty private String username; + @JsonProperty + private String password; + + @JsonProperty + private String confirmPassword; + public String getUsername() { return username; } @@ -14,4 +20,19 @@ public String getUsername() { public void setUsername(String username) { this.username = username; } + + public void setPassword(String password) { + this.password = password; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + public String getPassword() { + return password; + } + + public String getConfirmPassword() { + return confirmPassword; + } } diff --git a/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationFilter.java b/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationFilter.java new file mode 100644 index 000000000..978c7b42a --- /dev/null +++ b/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationFilter.java @@ -0,0 +1,83 @@ +package com.example.demo.security; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.auth0.jwt.algorithms.Algorithm; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import com.auth0.jwt.JWT; +import com.example.demo.model.persistence.User; +import com.fasterxml.jackson.databind.ObjectMapper; + +import static com.auth0.jwt.algorithms.Algorithm.HMAC512; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import com.auth0.jwt.JWT; +import com.example.demo.model.persistence.User; +import com.fasterxml.jackson.databind.ObjectMapper; + +import static com.auth0.jwt.algorithms.Algorithm.HMAC512; + +public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + + private AuthenticationManager authenticationManager; + + public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest req, + HttpServletResponse res) throws AuthenticationException { + try { + User credentials = new ObjectMapper() + .readValue(req.getInputStream(), User.class); + + return authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken( + credentials.getUsername(), + credentials.getPassword(), + new ArrayList<>())); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + protected void successfulAuthentication(HttpServletRequest req, + HttpServletResponse res, + FilterChain chain, + Authentication auth) throws IOException, ServletException { + + String token = JWT.create() + .withSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername()) + .withExpiresAt(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME)) + .sign(HMAC512(SecurityConstants.SECRET.getBytes())); + res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token); + } +} diff --git a/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationVerficationFilter.java b/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationVerficationFilter.java new file mode 100644 index 000000000..3d85ab18c --- /dev/null +++ b/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationVerficationFilter.java @@ -0,0 +1,58 @@ +package com.example.demo.security; + +import java.io.IOException; +import java.util.ArrayList; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.stereotype.Component; + +import com.auth0.jwt.JWT; + +import static com.auth0.jwt.algorithms.Algorithm.HMAC512; + +@Component +public class JWTAuthenticationVerficationFilter extends BasicAuthenticationFilter { + + public JWTAuthenticationVerficationFilter(AuthenticationManager authManager) { + super(authManager); + } + + @Override + protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) + throws IOException, ServletException { + String header = req.getHeader(SecurityConstants.HEADER_STRING); + + if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) { + chain.doFilter(req, res); + return; + } + + UsernamePasswordAuthenticationToken authentication = getAuthentication(req); + + SecurityContextHolder.getContext().setAuthentication(authentication); + chain.doFilter(req, res); + } + + private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req) { + String token = req.getHeader(SecurityConstants.HEADER_STRING); + if (token != null) { + String user = JWT.require(HMAC512(SecurityConstants.SECRET.getBytes())).build() + .verify(token.replace(SecurityConstants.TOKEN_PREFIX, "")) + .getSubject(); + if (user != null) { + return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>()); + } + return null; + } + return null; + } + +} diff --git a/starter_code/src/main/java/com/example/demo/security/SecurityConstants.java b/starter_code/src/main/java/com/example/demo/security/SecurityConstants.java new file mode 100644 index 000000000..3f7cb5455 --- /dev/null +++ b/starter_code/src/main/java/com/example/demo/security/SecurityConstants.java @@ -0,0 +1,10 @@ +package com.example.demo.security; + +public class SecurityConstants { + + public static final String SECRET = "oursecretkey"; + public static final long EXPIRATION_TIME = 864_000_000; // 10 days + public static final String TOKEN_PREFIX = "Bearer "; + public static final String HEADER_STRING = "Authorization"; + public static final String SIGN_UP_URL = "/api/user/create"; +} diff --git a/starter_code/src/main/java/com/example/demo/security/UserDetailsServiceImpl.java b/starter_code/src/main/java/com/example/demo/security/UserDetailsServiceImpl.java new file mode 100644 index 000000000..984331456 --- /dev/null +++ b/starter_code/src/main/java/com/example/demo/security/UserDetailsServiceImpl.java @@ -0,0 +1,28 @@ +package com.example.demo.security; + +import java.util.Collections; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import com.example.demo.model.persistence.User; +import com.example.demo.model.persistence.repositories.UserRepository; + +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + @Autowired + private UserRepository userRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + User user = userRepository.findByUsername(username); + if (user == null) { + throw new UsernameNotFoundException(username); + } + return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), Collections.emptyList()); + } +} diff --git a/starter_code/src/main/java/com/example/demo/security/WebSecurityConfiguration.java b/starter_code/src/main/java/com/example/demo/security/WebSecurityConfiguration.java new file mode 100644 index 000000000..da0e7f3da --- /dev/null +++ b/starter_code/src/main/java/com/example/demo/security/WebSecurityConfiguration.java @@ -0,0 +1,46 @@ +package com.example.demo.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +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; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +@EnableWebSecurity +public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + private UserDetailsServiceImpl userDetailsService; + private BCryptPasswordEncoder bCryptPasswordEncoder; + + public WebSecurityConfiguration(UserDetailsServiceImpl userDetailsService, + BCryptPasswordEncoder bCryptPasswordEncoder) { + this.userDetailsService = userDetailsService; + this.bCryptPasswordEncoder = bCryptPasswordEncoder; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.cors().and().csrf().disable().authorizeRequests() + .antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL).permitAll() + .anyRequest().authenticated() + .and() + .addFilter(new JWTAuthenticationFilter(authenticationManager())) + .addFilter(new JWTAuthenticationVerficationFilter(authenticationManager())) + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + } + + @Override + @Bean + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Override + public void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder); + } +} diff --git a/starter_code/target/classes/com/example/demo/SareetaApplication.class b/starter_code/target/classes/com/example/demo/SareetaApplication.class index 388a4f339..50d02e8d7 100644 Binary files a/starter_code/target/classes/com/example/demo/SareetaApplication.class and b/starter_code/target/classes/com/example/demo/SareetaApplication.class differ diff --git a/starter_code/target/classes/com/example/demo/controllers/CartController.class b/starter_code/target/classes/com/example/demo/controllers/CartController.class index 2626d1e2e..141ade405 100644 Binary files a/starter_code/target/classes/com/example/demo/controllers/CartController.class and b/starter_code/target/classes/com/example/demo/controllers/CartController.class differ diff --git a/starter_code/target/classes/com/example/demo/controllers/ItemController.class b/starter_code/target/classes/com/example/demo/controllers/ItemController.class index 52ac532e6..82e4a5b36 100644 Binary files a/starter_code/target/classes/com/example/demo/controllers/ItemController.class and b/starter_code/target/classes/com/example/demo/controllers/ItemController.class differ diff --git a/starter_code/target/classes/com/example/demo/controllers/OrderController.class b/starter_code/target/classes/com/example/demo/controllers/OrderController.class index e8501d299..1a61b3f7e 100644 Binary files a/starter_code/target/classes/com/example/demo/controllers/OrderController.class and b/starter_code/target/classes/com/example/demo/controllers/OrderController.class differ diff --git a/starter_code/target/classes/com/example/demo/controllers/UserController.class b/starter_code/target/classes/com/example/demo/controllers/UserController.class index 04bb6840f..3dabda79b 100644 Binary files a/starter_code/target/classes/com/example/demo/controllers/UserController.class and b/starter_code/target/classes/com/example/demo/controllers/UserController.class differ diff --git a/starter_code/target/classes/com/example/demo/model/persistence/Item.class b/starter_code/target/classes/com/example/demo/model/persistence/Item.class index 5bce916c6..3b71089e1 100644 Binary files a/starter_code/target/classes/com/example/demo/model/persistence/Item.class and b/starter_code/target/classes/com/example/demo/model/persistence/Item.class differ diff --git a/starter_code/target/classes/com/example/demo/model/persistence/User.class b/starter_code/target/classes/com/example/demo/model/persistence/User.class index b4fe72542..328dedc01 100644 Binary files a/starter_code/target/classes/com/example/demo/model/persistence/User.class and b/starter_code/target/classes/com/example/demo/model/persistence/User.class differ diff --git a/starter_code/target/classes/com/example/demo/model/persistence/repositories/CartRepository.class b/starter_code/target/classes/com/example/demo/model/persistence/repositories/CartRepository.class index 528fcb014..07321a06e 100644 Binary files a/starter_code/target/classes/com/example/demo/model/persistence/repositories/CartRepository.class and b/starter_code/target/classes/com/example/demo/model/persistence/repositories/CartRepository.class differ diff --git a/starter_code/target/classes/com/example/demo/model/persistence/repositories/ItemRepository.class b/starter_code/target/classes/com/example/demo/model/persistence/repositories/ItemRepository.class index fcdea8cef..41c95fcb9 100644 Binary files a/starter_code/target/classes/com/example/demo/model/persistence/repositories/ItemRepository.class and b/starter_code/target/classes/com/example/demo/model/persistence/repositories/ItemRepository.class differ diff --git a/starter_code/target/classes/com/example/demo/model/persistence/repositories/OrderRepository.class b/starter_code/target/classes/com/example/demo/model/persistence/repositories/OrderRepository.class index e47ebf196..ad5b636c6 100644 Binary files a/starter_code/target/classes/com/example/demo/model/persistence/repositories/OrderRepository.class and b/starter_code/target/classes/com/example/demo/model/persistence/repositories/OrderRepository.class differ diff --git a/starter_code/target/classes/com/example/demo/model/persistence/repositories/UserRepository.class b/starter_code/target/classes/com/example/demo/model/persistence/repositories/UserRepository.class index 54313b79f..bcaa1059e 100644 Binary files a/starter_code/target/classes/com/example/demo/model/persistence/repositories/UserRepository.class and b/starter_code/target/classes/com/example/demo/model/persistence/repositories/UserRepository.class differ diff --git a/starter_code/target/classes/com/example/demo/model/requests/CreateUserRequest.class b/starter_code/target/classes/com/example/demo/model/requests/CreateUserRequest.class index 2a2337627..99994e1e9 100644 Binary files a/starter_code/target/classes/com/example/demo/model/requests/CreateUserRequest.class and b/starter_code/target/classes/com/example/demo/model/requests/CreateUserRequest.class differ