diff --git a/src/main/java/com/olympus/hermione/security/config/CorsConfig.java b/src/main/java/com/olympus/hermione/security/config/CorsConfig.java new file mode 100644 index 0000000..1684c98 --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/config/CorsConfig.java @@ -0,0 +1,22 @@ +package com.olympus.hermione.security.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + @Value("${ariadne.fe.url}") + private String ariadne_frontend_url; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins(ariadne_frontend_url) + .allowedHeaders("*") + .allowedMethods("GET", "POST", "PUT", "DELETE","OPTIONS"); + } +} + \ No newline at end of file diff --git a/src/main/java/com/olympus/hermione/security/config/SecurityConfig.java b/src/main/java/com/olympus/hermione/security/config/SecurityConfig.java new file mode 100644 index 0000000..d1fc2a7 --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/config/SecurityConfig.java @@ -0,0 +1,88 @@ +package com.olympus.hermione.security.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import org.springframework.security.authentication.AuthenticationManager; + +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import com.olympus.hermione.security.filter.JwtTokenFilter; +import com.olympus.hermione.security.services.CustomUserDetailsService; + +@EnableWebSecurity +@Configuration +public class SecurityConfig { + + @Autowired + CustomUserDetailsService userDetailsService; + +/* @Autowired + private AuthEntryPointJwt unauthorizedHandler; +*/ + @Bean + public JwtTokenFilter authenticationJwtTokenFilter() { + return new JwtTokenFilter(); + } + +//@Override +//public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { +// authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); +//} + + @Bean + public DaoAuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + + authProvider.setUserDetailsService(userDetailsService); + authProvider.setPasswordEncoder(passwordEncoder()); + + return authProvider; + } + +//@Bean +//@Override +//public AuthenticationManager authenticationManagerBean() throws Exception { +// return super.authenticationManagerBean(); +//} + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { + return authConfig.getAuthenticationManager(); + } + + + @Bean + PasswordEncoder passwordEncoder() { + return NoOpPasswordEncoder.getInstance(); + } + + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf(csrf -> csrf.disable()) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> auth.requestMatchers("/api/auth/**").permitAll() + .requestMatchers("/api/test/**").permitAll() + .requestMatchers("/test/**").permitAll() + .anyRequest().permitAll());//.authenticated()); + + http.authenticationProvider(authenticationProvider()); + + http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + +} + + diff --git a/src/main/java/com/olympus/hermione/security/controllers/AuthController.java b/src/main/java/com/olympus/hermione/security/controllers/AuthController.java new file mode 100644 index 0000000..7542f13 --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/controllers/AuthController.java @@ -0,0 +1,87 @@ +package com.olympus.hermione.security.controllers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.olympus.hermione.security.dto.AuthenticationRequest; +import com.olympus.hermione.security.dto.AuthenticationResponse; +import com.olympus.hermione.security.dto.FetchUserResponse; +import com.olympus.hermione.security.entity.User; +import com.olympus.hermione.security.utility.JwtTokenProvider; + +@RestController +@RequestMapping("/api/auth") +public class AuthController { + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + @PostMapping("/login") + public ResponseEntity authenticateUser(@RequestBody AuthenticationRequest authenticationRequest) { + + Authentication authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken( + authenticationRequest.getUsername(), + authenticationRequest.getPassword() + ) + ); + + SecurityContextHolder.getContext().setAuthentication(authentication); + String jwt = jwtTokenProvider.createToken(authentication); + + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add("authorization", jwt); + httpHeaders.add("access-control-expose-headers", "authorization"); + AuthenticationResponse authenticationResponse = new AuthenticationResponse(jwt, (User) authentication.getPrincipal()); + + return ResponseEntity.ok().headers(httpHeaders).body(authenticationResponse); + + } + + @GetMapping("/fetch-user") + public FetchUserResponse fetchUser(Authentication authentication) { + User principal = (User) authentication.getPrincipal(); + principal.setPassword(null); + FetchUserResponse fetchUserResponse = new FetchUserResponse(); + fetchUserResponse.setData(principal); + return fetchUserResponse; + + } + + + @GetMapping("/refresh-token") + public ResponseEntity refreshToken(Authentication authentication) { + + + SecurityContextHolder.getContext().setAuthentication(authentication); + String jwt = jwtTokenProvider.createToken(authentication); + + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add("authorization", jwt); + httpHeaders.add("access-control-expose-headers", "authorization"); + AuthenticationResponse authenticationResponse = new AuthenticationResponse(jwt, (User) authentication.getPrincipal()); + + return ResponseEntity.ok().headers(httpHeaders).body(authenticationResponse); + } + + @GetMapping("/test") + public ResponseEntity test() { + return ResponseEntity.ok(" you have access now "); + } +} + diff --git a/src/main/java/com/olympus/hermione/security/dto/AuthenticationRequest.java b/src/main/java/com/olympus/hermione/security/dto/AuthenticationRequest.java new file mode 100644 index 0000000..6b43efc --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/dto/AuthenticationRequest.java @@ -0,0 +1,17 @@ +package com.olympus.hermione.security.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class AuthenticationRequest { + + private String username; + private String password; +} + diff --git a/src/main/java/com/olympus/hermione/security/dto/AuthenticationResponse.java b/src/main/java/com/olympus/hermione/security/dto/AuthenticationResponse.java new file mode 100644 index 0000000..6e65091 --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/dto/AuthenticationResponse.java @@ -0,0 +1,20 @@ +package com.olympus.hermione.security.dto; + +import com.olympus.hermione.security.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class AuthenticationResponse { + + private String accessToken; + private User data; + +} + diff --git a/src/main/java/com/olympus/hermione/security/dto/FetchUserResponse.java b/src/main/java/com/olympus/hermione/security/dto/FetchUserResponse.java new file mode 100644 index 0000000..ec9e324 --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/dto/FetchUserResponse.java @@ -0,0 +1,16 @@ +package com.olympus.hermione.security.dto; + +import com.olympus.hermione.security.entity.User; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class FetchUserResponse { + private User data; + + + +} + diff --git a/src/main/java/com/olympus/hermione/security/entity/Role.java b/src/main/java/com/olympus/hermione/security/entity/Role.java new file mode 100644 index 0000000..2216e14 --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/entity/Role.java @@ -0,0 +1,6 @@ +package com.olympus.hermione.security.entity; + +public enum Role { + ADMIN,USER +} + diff --git a/src/main/java/com/olympus/hermione/security/entity/User.java b/src/main/java/com/olympus/hermione/security/entity/User.java new file mode 100644 index 0000000..4bbd870 --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/entity/User.java @@ -0,0 +1,41 @@ +package com.olympus.hermione.security.entity; +import java.util.Collection; + +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import java.util.List; + + +@Document(collection = "users") +@Getter @Setter +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class User implements UserDetails{ + @Id + private String id; + private String username; + private String password; + private String name; + private String surname; + + private Role role; + + @Override + public Collection getAuthorities() { + return List.of(new SimpleGrantedAuthority(role.name())); + } + + +} diff --git a/src/main/java/com/olympus/hermione/security/filter/JwtTokenFilter.java b/src/main/java/com/olympus/hermione/security/filter/JwtTokenFilter.java new file mode 100644 index 0000000..dd6dd26 --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/filter/JwtTokenFilter.java @@ -0,0 +1,54 @@ +package com.olympus.hermione.security.filter; + +import java.io.IOException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.olympus.hermione.security.utility.JwtTokenProvider; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +@Component +@AllArgsConstructor +@NoArgsConstructor +public class JwtTokenFilter extends OncePerRequestFilter { + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + @Autowired + private UserDetailsService userDetailsService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + + String token = jwtTokenProvider.resolveToken(request); + + if (token != null && jwtTokenProvider.validateToken(token)) { + + UserDetails userDetails = userDetailsService.loadUserByUsername(jwtTokenProvider.getUsername(token)); + + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + filterChain.doFilter(request, response); + } + + +} + diff --git a/src/main/java/com/olympus/hermione/security/repository/UserRepository.java b/src/main/java/com/olympus/hermione/security/repository/UserRepository.java new file mode 100644 index 0000000..9d041d5 --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/repository/UserRepository.java @@ -0,0 +1,14 @@ +package com.olympus.hermione.security.repository; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.mongodb.repository.Query; +import org.springframework.stereotype.Repository; + +import com.olympus.hermione.security.entity.User; + +@Repository +public interface UserRepository extends MongoRepository { + + @Query("{username:'?0'}") + User findUserByUsername(String username); +} \ No newline at end of file diff --git a/src/main/java/com/olympus/hermione/security/services/CustomUserDetailsService.java b/src/main/java/com/olympus/hermione/security/services/CustomUserDetailsService.java new file mode 100644 index 0000000..977695b --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/services/CustomUserDetailsService.java @@ -0,0 +1,35 @@ +package com.olympus.hermione.security.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +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.olympus.hermione.security.entity.User; +import com.olympus.hermione.security.repository.UserRepository; + +@Configuration +@Service +public class CustomUserDetailsService implements UserDetailsService { + + @Autowired + private UserRepository userRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + try { + User user = userRepository.findUserByUsername(username); + if(user != null){ + return user; + }else{ + throw new Exception("user Not found "); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/olympus/hermione/security/utility/JwtAuthenticationEntryPoint.java b/src/main/java/com/olympus/hermione/security/utility/JwtAuthenticationEntryPoint.java new file mode 100644 index 0000000..affcdbc --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/utility/JwtAuthenticationEntryPoint.java @@ -0,0 +1,30 @@ +package com.olympus.hermione.security.utility; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import io.jsonwebtoken.io.IOException; + + +@Component +public class JwtAuthenticationEntryPoint extends BasicAuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authException) throws IOException, java.io.IOException { + + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setContentType("application/json"); + response.getWriter().write("{ \"message\": \"" + authException.getMessage() + "\" }"); + } + + @Override + public void afterPropertiesSet() { + setRealmName("JWT Authentication"); + super.afterPropertiesSet(); + } +} + diff --git a/src/main/java/com/olympus/hermione/security/utility/JwtTokenProvider.java b/src/main/java/com/olympus/hermione/security/utility/JwtTokenProvider.java new file mode 100644 index 0000000..8bf7c90 --- /dev/null +++ b/src/main/java/com/olympus/hermione/security/utility/JwtTokenProvider.java @@ -0,0 +1,87 @@ +package com.olympus.hermione.security.utility; + +import org.springframework.stereotype.Component; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.util.StringUtils; +import java.security.Key; +import java.util.Date; + +@Component +@Slf4j +public class JwtTokenProvider { + + Key key = Keys.secretKeyFor(SignatureAlgorithm.HS512); + + + public static final String SECRET = "5367566B59703373367639792F423F4528482B4D6251655468576D5A713474375367566B59703373367639792F423F4528482B4D6251655468576D5A71347437"; + + + private Key getSignKey() { + byte[] keyBytes = Decoders.BASE64.decode(SECRET); + return Keys.hmacShaKeyFor(keyBytes); + } + + + public String createToken(Authentication authentication) { + + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + Date now = new Date(); + Date expiryDate = new Date(now.getTime() + 3600000); + + return Jwts.builder() + .setSubject(userDetails.getUsername()) + .setIssuedAt(new Date()) + .setExpiration(expiryDate) + .signWith(getSignKey(), SignatureAlgorithm.HS512) + //.signWith(SignatureAlgorithm.HS512, SECRET) + .compact(); + } + + + public String resolveToken(HttpServletRequest request) { + + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); + } + return null; + } + + // Check if the token is valid and not expired + public boolean validateToken(String token) { + + try { + Jwts.parser().setSigningKey(getSignKey()).parseClaimsJws(token); + return true; + } catch (MalformedJwtException ex) { + log.error("Invalid JWT token"); + } catch (ExpiredJwtException ex) { + log.error("Expired JWT token"); + } catch (UnsupportedJwtException ex) { + log.error("Unsupported JWT token"); + } catch (IllegalArgumentException ex) { + log.error("JWT claims string is empty"); + } catch (SignatureException e) { + log.error("there is an error with the signature of you token "); + } + return false; + } + + // Extract the username from the JWT token + public String getUsername(String token) { + + return Jwts.parser() + .setSigningKey(getSignKey()) + .parseClaimsJws(token) + .getBody() + .getSubject(); + } +}