diff --git a/Backend/build.gradle b/Backend/build.gradle index fd128f5c0d61daaac6c83f959a924f2f20f75f58..d67b1d3ec9970b85fb098203e3e7acc029ef4bcb 100644 --- a/Backend/build.gradle +++ b/Backend/build.gradle @@ -21,6 +21,9 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' implementation 'org.springframework.boot:spring-boot-starter-web' + compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '2.3.0.RELEASE' + compile group: 'com.auth0', name: 'java-jwt', version: '3.10.3' + compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation('org.springframework.boot:spring-boot-starter-test') { diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/PecuniaApplication.java b/Backend/src/main/java/com/mobilecomputing/pecunia/PecuniaApplication.java index 1627f1cc48f3aa15ef0ba617607932f41df77acc..ba3faae8a3eb775a04dc89af5966dced328c3e03 100644 --- a/Backend/src/main/java/com/mobilecomputing/pecunia/PecuniaApplication.java +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/PecuniaApplication.java @@ -2,10 +2,17 @@ package com.mobilecomputing.pecunia; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @SpringBootApplication public class PecuniaApplication { + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder(){ + return new BCryptPasswordEncoder(); + } + public static void main(String[] args) { SpringApplication.run(PecuniaApplication.class, args); } diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/controller/UserController.java b/Backend/src/main/java/com/mobilecomputing/pecunia/controller/UserController.java index d472e1bc851a2d3f56f367915d45b5654bb9d6e4..6e302bed566703f8891644ecbd862b719c6b3a1d 100644 --- a/Backend/src/main/java/com/mobilecomputing/pecunia/controller/UserController.java +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/controller/UserController.java @@ -1,10 +1,10 @@ package com.mobilecomputing.pecunia.controller; -import com.mobilecomputing.pecunia.model.User; +import com.mobilecomputing.pecunia.model.ApplicationUser; import com.mobilecomputing.pecunia.repository.UserRepository; import org.bson.types.ObjectId; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.web.bind.annotation.*; @RestController @@ -13,6 +13,8 @@ public class UserController { @Autowired UserRepository userRepository; + BCryptPasswordEncoder bCryptPasswordEncoder; + @GetMapping("/getById") public String getUserById(@RequestParam String id){ return String.valueOf(userRepository.findById(new ObjectId(id))); @@ -27,17 +29,11 @@ public class UserController { return String.valueOf(userRepository.findAll()); } - @PostMapping("/registrateUser") - public String registrateUser(@RequestParam String eMail, @RequestParam String name, @RequestParam String surname, - @RequestParam String password){ - User user = new User(); - user.seteMail(eMail); // überprüfen ob email schon vergeben ist - user.setName(name); - user.setSurname(surname); - user.setPassword(password); + @PostMapping("/sign-up") + public void signUp(@RequestBody ApplicationUser user){ + user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); userRepository.save(user); - return "ok?"; } @DeleteMapping("/deleteUser") diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/model/User.java b/Backend/src/main/java/com/mobilecomputing/pecunia/model/ApplicationUser.java similarity index 94% rename from Backend/src/main/java/com/mobilecomputing/pecunia/model/User.java rename to Backend/src/main/java/com/mobilecomputing/pecunia/model/ApplicationUser.java index f04c9946c69fdbce2be2c487c507c220c45bc4e6..b8c00a7720219d7be019fd13465c4e4721b30efa 100644 --- a/Backend/src/main/java/com/mobilecomputing/pecunia/model/User.java +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/model/ApplicationUser.java @@ -2,7 +2,7 @@ package com.mobilecomputing.pecunia.model; import org.springframework.data.annotation.Id; -public class User { +public class ApplicationUser { @Id private String eMail; @@ -10,7 +10,7 @@ public class User { private String surname; private String password; - public String geteMail() { + public String getEMail() { return eMail; } diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/model/Transaction.java b/Backend/src/main/java/com/mobilecomputing/pecunia/model/Transaction.java new file mode 100644 index 0000000000000000000000000000000000000000..26fe1833b775519800aa0774525f590d24a87b03 --- /dev/null +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/model/Transaction.java @@ -0,0 +1,10 @@ +package com.mobilecomputing.pecunia.model; + +import org.springframework.data.annotation.Id; + +public class Transaction { + + @Id + private String transactionId; + +} diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/model/Trip.java b/Backend/src/main/java/com/mobilecomputing/pecunia/model/Trip.java index 6132067564dad02121bae4db983ec44f6712dbe6..9a5c0424c3c77074c1c01022eac4ffbcbc141537 100644 --- a/Backend/src/main/java/com/mobilecomputing/pecunia/model/Trip.java +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/model/Trip.java @@ -11,9 +11,9 @@ public class Trip { private String tripName; private Date startOfTrip; private Date endOfTrip; - private List<User> tripParticipants; + private List<ApplicationUser> tripParticipants; - public Trip(String tripId, String tripName, Date startOfTrip, Date endOfTrip, List<User> tripParticipants) { + public Trip(String tripId, String tripName, Date startOfTrip, Date endOfTrip, List<ApplicationUser> tripParticipants) { this.tripId = tripId; this.tripName = tripName; this.startOfTrip = startOfTrip; @@ -53,11 +53,11 @@ public class Trip { this.endOfTrip = endOfTrip; } - public List<User> getTripParticipants() { + public List<ApplicationUser> getTripParticipants() { return tripParticipants; } - public void setTripParticipants(List<User> tripParticipants) { + public void setTripParticipants(List<ApplicationUser> tripParticipants) { this.tripParticipants = tripParticipants; } } diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/repository/UserRepository.java b/Backend/src/main/java/com/mobilecomputing/pecunia/repository/UserRepository.java index ccc2662873d7d4e8aaaca4265101594a9016300c..1d9dfabcb7b2915ba39995e218a0c7db0bda0626 100644 --- a/Backend/src/main/java/com/mobilecomputing/pecunia/repository/UserRepository.java +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/repository/UserRepository.java @@ -1,9 +1,8 @@ package com.mobilecomputing.pecunia.repository; -import com.mobilecomputing.pecunia.model.Trip; -import com.mobilecomputing.pecunia.model.User; +import com.mobilecomputing.pecunia.model.ApplicationUser; import org.bson.types.ObjectId; import org.springframework.data.repository.CrudRepository; -public interface UserRepository extends CrudRepository<User, ObjectId> { +public interface UserRepository extends CrudRepository<ApplicationUser, ObjectId> { } diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/security/JWTAuthenticationFilter.java b/Backend/src/main/java/com/mobilecomputing/pecunia/security/JWTAuthenticationFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..50316ff235f1628e77d7c1d74962130262d13e8f --- /dev/null +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/security/JWTAuthenticationFilter.java @@ -0,0 +1,63 @@ +package com.mobilecomputing.pecunia.security; + +import com.auth0.jwt.JWT; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mobilecomputing.pecunia.model.ApplicationUser; +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 javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; + +import static com.auth0.jwt.algorithms.Algorithm.HMAC512; +import static com.mobilecomputing.pecunia.security.SecurityConstraints.*; + +/** + * https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/#User-Authentication-and-Authorization-on-Spring-Boot + */ +public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + private AuthenticationManager authenticationManager; + + public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + System.out.println("JWT AUTHENTICATION created"); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, + HttpServletResponse response) throws AuthenticationException { + try { + System.out.println("attemptAuthentication"); + ApplicationUser creds = new ObjectMapper().readValue(request.getInputStream(), ApplicationUser.class); + return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( + creds.getEMail(), + creds.getPassword(), + new ArrayList<>())); + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + protected void successfulAuthentication(HttpServletRequest request, + HttpServletResponse response, + FilterChain chain, + Authentication authResult) throws IOException, ServletException { + System.out.println("successfulAuthentication"); + String token = JWT.create() + .withSubject(((ApplicationUser)authResult.getPrincipal()).getEMail()) + .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) + .sign(HMAC512(SECRET.getBytes())); + System.out.println(token); + response.addHeader(HEADER_STRING,TOKEN_PREFIX+token); + } +} diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/security/JWTAuthorizationFilter.java b/Backend/src/main/java/com/mobilecomputing/pecunia/security/JWTAuthorizationFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..0bf97357aa73ceaad8379b4eb1075ae9bf18219f --- /dev/null +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/security/JWTAuthorizationFilter.java @@ -0,0 +1,61 @@ +package com.mobilecomputing.pecunia.security; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; + +import static com.mobilecomputing.pecunia.security.SecurityConstraints.*; + +public class JWTAuthorizationFilter extends BasicAuthenticationFilter { + + public JWTAuthorizationFilter(AuthenticationManager authenticationManager) { + super(authenticationManager); + } + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain chain) throws IOException, ServletException { + String header = request.getHeader(HEADER_STRING); + + System.out.println("doFilterInternal"); + + if(header == null || !header.startsWith(TOKEN_PREFIX)){ + chain.doFilter(request,response); + return; + } + + UsernamePasswordAuthenticationToken authentication = getAuthentication(request); + + SecurityContextHolder.getContext().setAuthentication(authentication); + chain.doFilter(request, response); + } + + private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request){ + String token = request.getHeader(HEADER_STRING); + System.out.println("JWT AUTHORIZATION username passwordauthToken drinn"); + if(token!=null){ + String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes())) + .build() + .verify(token.replace(TOKEN_PREFIX, "")) + .getSubject(); + + if(user!=null){ + return new UsernamePasswordAuthenticationToken(user,null, new ArrayList<>()); + } + return null; + } + return null; + } +} diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/security/SecurityConstraints.java b/Backend/src/main/java/com/mobilecomputing/pecunia/security/SecurityConstraints.java new file mode 100644 index 0000000000000000000000000000000000000000..5ab1ef2e64c1c2cb595338c237c288bdbd7d3332 --- /dev/null +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/security/SecurityConstraints.java @@ -0,0 +1,9 @@ +package com.mobilecomputing.pecunia.security; + +public class SecurityConstraints { + public static final String SECRET = "SecretKeyToGenJWTs"; + 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 = "/users/sign-up"; +} diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/security/UserDetailsServiceImpl.java b/Backend/src/main/java/com/mobilecomputing/pecunia/security/UserDetailsServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..d15055b3fa0efbc0fbf24bbf77e410d41851dc46 --- /dev/null +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/security/UserDetailsServiceImpl.java @@ -0,0 +1,35 @@ +package com.mobilecomputing.pecunia.security; + +import com.mobilecomputing.pecunia.model.ApplicationUser; +import com.mobilecomputing.pecunia.repository.UserRepository; +import org.bson.types.ObjectId; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import java.util.Collections; +import java.util.Optional; + +public class UserDetailsServiceImpl implements UserDetailsService { + + @Autowired + private UserRepository userRepository; + + public UserDetailsServiceImpl(UserRepository userRepository){ + System.out.println("userDetails Service created"); + this.userRepository=userRepository; + } + + @Override + public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException { + System.out.println("loadbyUsername"); + Optional<ApplicationUser> user = userRepository.findById(new ObjectId(userEmail)); + if(user ==null){ + throw new UsernameNotFoundException(user.toString()); + } + ApplicationUser applicationUser = user.get(); + return new User(applicationUser.getEMail(),applicationUser.getPassword(), Collections.emptyList()); + } +} diff --git a/Backend/src/main/java/com/mobilecomputing/pecunia/security/WebSecurity.java b/Backend/src/main/java/com/mobilecomputing/pecunia/security/WebSecurity.java new file mode 100644 index 0000000000000000000000000000000000000000..e2a20698fb621e86d9dc25bec40691c051591102 --- /dev/null +++ b/Backend/src/main/java/com/mobilecomputing/pecunia/security/WebSecurity.java @@ -0,0 +1,51 @@ +package com.mobilecomputing.pecunia.security; + +import org.springframework.context.annotation.Bean; +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.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import static com.mobilecomputing.pecunia.security.SecurityConstraints.SIGN_UP_URL; + + +public class WebSecurity extends WebSecurityConfigurerAdapter { + private UserDetailsServiceImpl userDetailsService; + private BCryptPasswordEncoder bCryptPasswordEncoder; + + public WebSecurity(UserDetailsServiceImpl userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) { + this.userDetailsService = userDetailsService; + this.bCryptPasswordEncoder = bCryptPasswordEncoder; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + System.out.println("web sec configure"); + http.cors().and().csrf().disable().authorizeRequests() + .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll() + .anyRequest().authenticated() + .and() + .addFilter(new JWTAuthenticationFilter(authenticationManager())) + .addFilter(new JWTAuthorizationFilter(authenticationManager())) + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + } + + @Override + public void configure(AuthenticationManagerBuilder auth) throws Exception { + System.out.println("web sec configure 2"); + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder); + } + + @Bean + CorsConfigurationSource corsConfigurationSource() { + System.out.println("web sec config cors"); + final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); + return source; + } +}