From 377786d0d8c52c328c0872209b8646fb8a3a7699 Mon Sep 17 00:00:00 2001 From: Hemmat Date: Thu, 16 Mar 2023 13:51:34 +0200 Subject: [PATCH 1/2] #131_add_security_feature --- retail-managment-system/rms-api/pom.xml | 32 +++++ .../rms/config/GlobalCorsConfiguration.java | 30 ----- .../com/rms/config/WebSecurityConfig.java | 125 +++++++++++++----- .../com/rms/repository/UserRepository.java | 13 ++ .../rms/repository/UserRoleRepository.java | 5 + .../com/rms/rest/handler/UserHandler.java | 32 ++--- .../java/com/rms/security/AuthController.java | 40 ++++++ .../java/com/rms/security/AuthHandler.java | 72 ++++++++++ .../security/CustomUserDetailsService.java | 51 +++++++ .../com/rms/security/LoginRequestDto.java | 19 +++ .../com/rms/security/LoginResponseDto.java | 17 +++ .../rms/security/RefreshTokenRequestDto.java | 8 ++ .../RestAuthenticationEntryPoint.java | 27 ++++ .../security/TokenAuthenticationFilter.java | 54 ++++++++ .../java/com/rms/security/TokenProvider.java | 80 +++++++++++ .../main/java/com/rms/security/UserDto.java | 17 +++ .../java/com/rms/service/UserRoleService.java | 5 + .../java/com/rms/service/UserService.java | 44 ++++-- .../src/main/resources/application.yml | 15 ++- 19 files changed, 589 insertions(+), 97 deletions(-) delete mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/config/GlobalCorsConfiguration.java create mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/security/AuthController.java create mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/security/AuthHandler.java create mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/security/CustomUserDetailsService.java create mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/security/LoginRequestDto.java create mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/security/LoginResponseDto.java create mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/security/RefreshTokenRequestDto.java create mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/security/RestAuthenticationEntryPoint.java create mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/security/TokenAuthenticationFilter.java create mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/security/TokenProvider.java create mode 100644 retail-managment-system/rms-api/src/main/java/com/rms/security/UserDto.java diff --git a/retail-managment-system/rms-api/pom.xml b/retail-managment-system/rms-api/pom.xml index c1a51307..1375d5d4 100644 --- a/retail-managment-system/rms-api/pom.xml +++ b/retail-managment-system/rms-api/pom.xml @@ -119,6 +119,38 @@ + + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + org.springframework.boot + spring-boot-starter-security + + + + + org.passay + passay + 1.3.1 + + + diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/config/GlobalCorsConfiguration.java b/retail-managment-system/rms-api/src/main/java/com/rms/config/GlobalCorsConfiguration.java deleted file mode 100644 index f20d7c7c..00000000 --- a/retail-managment-system/rms-api/src/main/java/com/rms/config/GlobalCorsConfiguration.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.rms.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; - -@Configuration -public class GlobalCorsConfiguration { - - public GlobalCorsConfiguration() { - super(); - } - - /** - * Bean to define global CORS. - * - * @return - */ - @Bean - public WebMvcConfigurer corsConfigurer() { - return new WebMvcConfigurerAdapter() { - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**").allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH"); - } - }; - } -} diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/config/WebSecurityConfig.java b/retail-managment-system/rms-api/src/main/java/com/rms/config/WebSecurityConfig.java index a953d901..80144831 100644 --- a/retail-managment-system/rms-api/src/main/java/com/rms/config/WebSecurityConfig.java +++ b/retail-managment-system/rms-api/src/main/java/com/rms/config/WebSecurityConfig.java @@ -1,36 +1,89 @@ -//package com.rms.config; -// -//import org.springframework.context.annotation.Bean; -//import org.springframework.context.annotation.Configuration; -//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.web.cors.CorsConfiguration; -//import org.springframework.web.cors.CorsConfigurationSource; -//import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -// -//@Configuration -//@EnableWebSecurity -//public class WebSecurityConfig extends WebSecurityConfigurerAdapter { -// -// protected void configure(HttpSecurity http) throws Exception { -// http.cors().configurationSource(corsConfigurationSource()).and().csrf().disable(); -// } -// -// @Bean -// public CorsConfigurationSource corsConfigurationSource() { -// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); -// CorsConfiguration config = new CorsConfiguration(); -// config.setAllowCredentials(true); -// config.addAllowedOrigin("*"); -// config.addAllowedHeader("Authorization"); -// config.addAllowedHeader("Content-Type"); -// config.addAllowedHeader("x-auth-token"); -// config.addAllowedHeader("If-None-Match"); -// config.addAllowedHeader("If-Match"); -// config.addExposedHeader("ETag"); -// config.addAllowedMethod("*"); -// source.registerCorsConfiguration("/**", config); -// return source; -// } -//} +package com.rms.config; +import com.rms.security.CustomUserDetailsService; +import com.rms.security.RestAuthenticationEntryPoint; +import com.rms.security.TokenAuthenticationFilter; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +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; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +@RequiredArgsConstructor +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + private final CustomUserDetailsService customUserDetailsService; + + private final RestAuthenticationEntryPoint restAuthenticationEntryPoint; + + + + + @Override + public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { + authenticationManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder()); + } + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public TokenAuthenticationFilter authenticationJwtTokenFilter() { + return new TokenAuthenticationFilter(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + + http.cors().configurationSource(corsConfigurationSource()).and().csrf().disable().exceptionHandling() + .authenticationEntryPoint(restAuthenticationEntryPoint); + http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + http.authorizeRequests().antMatchers("/auth/**").permitAll(); + //for swagger + http.authorizeRequests().antMatchers("/v3/api-docs/*", "/v3/api-docs", "/swagger-ui.html", "/swagger-ui/*").permitAll(); + http.authorizeRequests().anyRequest().authenticated(); + http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); + } + + @Override + public void configure(WebSecurity web) throws Exception { + + } + + + @Bean + CorsConfigurationSource corsConfigurationSource() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration corsConfiguration = new CorsConfiguration().applyPermitDefaultValues(); + corsConfiguration.addAllowedMethod(HttpMethod.PUT); + corsConfiguration.addAllowedMethod(HttpMethod.DELETE); + corsConfiguration.addAllowedMethod(HttpMethod.OPTIONS); + corsConfiguration.addAllowedMethod(HttpMethod.PATCH); + source.registerCorsConfiguration("/**", corsConfiguration); + return source; + } +} diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/repository/UserRepository.java b/retail-managment-system/rms-api/src/main/java/com/rms/repository/UserRepository.java index 6ec9c6b0..94c1dc22 100644 --- a/retail-managment-system/rms-api/src/main/java/com/rms/repository/UserRepository.java +++ b/retail-managment-system/rms-api/src/main/java/com/rms/repository/UserRepository.java @@ -1,6 +1,8 @@ package com.rms.repository; import com.rms.domain.security.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -10,6 +12,17 @@ @Repository public interface UserRepository extends JpaRepository { + + + @Query(value = "SELECT u FROM User u LEFT JOIN FETCH u.employee WHERE u.id= :id") + Optional findById(@Param("id") Integer id); + @Query("select u from User u where u.userName = :userName") Optional findByUserName(@Param("userName") String userName); + + + @Query(value = "Select u.userName FROM User u WHERE u.userName= :username ") + Optional checkUsername(@Param("username") String username); + + } diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/repository/UserRoleRepository.java b/retail-managment-system/rms-api/src/main/java/com/rms/repository/UserRoleRepository.java index 19024f3d..99e231c5 100644 --- a/retail-managment-system/rms-api/src/main/java/com/rms/repository/UserRoleRepository.java +++ b/retail-managment-system/rms-api/src/main/java/com/rms/repository/UserRoleRepository.java @@ -1,5 +1,6 @@ package com.rms.repository; +import com.rms.domain.security.Role; import com.rms.domain.security.UserRole; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -7,6 +8,8 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; + +import java.util.List; import java.util.Optional; @Repository @@ -16,6 +19,8 @@ public interface UserRoleRepository extends JpaRepository { Optional findRoleIdByUserIdAndRoleId(@Param("userId") Integer userId, @Param("roleId") Integer roleId); + @Query(value = "SELECT ur.role FROM UserRole ur WHERE ur.user.userName= :username") + List findByUserName(@Param("username") String username); @Query(value = "SELECT ur FROM UserRole ur JOIN FETCH ur.role WHERE ur.user.id= :userId", countQuery = "SELECT COUNT(ur) FROM UserRole ur WHERE ur.user.id= :userId") diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/rest/handler/UserHandler.java b/retail-managment-system/rms-api/src/main/java/com/rms/rest/handler/UserHandler.java index 747e9167..980c87af 100644 --- a/retail-managment-system/rms-api/src/main/java/com/rms/rest/handler/UserHandler.java +++ b/retail-managment-system/rms-api/src/main/java/com/rms/rest/handler/UserHandler.java @@ -1,13 +1,17 @@ package com.rms.rest.handler; import com.rms.domain.core.Employee; +import com.rms.domain.security.Role; import com.rms.domain.security.User; +import com.rms.domain.security.UserRole; import com.rms.rest.dto.UserDto; import com.rms.rest.dto.common.PaginatedResultDto; import com.rms.rest.exception.*; import com.rms.rest.modelmapper.UserMapper; import com.rms.rest.modelmapper.common.PaginationMapper; import com.rms.service.EmployeeService; +import com.rms.service.RoleService; +import com.rms.service.UserRoleService; import com.rms.service.UserService; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; @@ -26,11 +30,8 @@ public class UserHandler { private UserMapper mapper ; private EmployeeService employeeService ; private PaginationMapper paginationMapper ; - - - - - + private UserRoleService userRoleService; + private RoleService roleService; public ResponseEntity getAll (Integer page , Integer size) { @@ -52,21 +53,22 @@ public ResponseEntity getById (Integer id) public ResponseEntity save(UserDto dto) { - Optional existedUsername = userService.findUserName(dto.getUserName()); - existedUsername.ifPresent(e -> { - throw new ResourceAlreadyExistsException(User.class.getSimpleName(), "User Name " , - dto.getUserName(), ErrorCodes.DUPLICATE_RESOURCE.getCode()); - }); - User user =mapper.toEntity(dto); - userService.save(user); - UserDto userDto = mapper.toDto(user); - return ResponseEntity.created(URI.create("/user/" + userDto.getId())).body(userDto); + Optional existedUsername= userService.findUsername(dto.getUserName()); + if (existedUsername.isPresent()){ + throw new ResourceAlreadyExistsException(User.class.getSimpleName(), + "username", dto.getUserName(), ErrorCodes.DUPLICATE_RESOURCE.getCode()); + } + + User user = mapper.toEntity(dto); + userService.save(user); + UserDto usersDto=mapper.toDto(user); + return ResponseEntity.created(URI.create("/user/" + usersDto.getId())).body(usersDto); } public ResponseEntity update (Integer id , UserDto userDto) { User existedUser= userService.getById(id).orElseThrow( ()-> new ResourceNotFoundException(User.class.getSimpleName() , id)); - Optional existedUsername = userService.findUserName(userDto.getUserName()); + Optional existedUsername = userService.getByUserName(userDto.getUserName()); if (existedUsername.isPresent() && !existedUsername.get().getId().equals(id)) { throw new ResourceAlreadyExistsException(User.class.getSimpleName(), "Username", userDto.getUserName(), ErrorCodes.DUPLICATE_RESOURCE.getCode()); diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/security/AuthController.java b/retail-managment-system/rms-api/src/main/java/com/rms/security/AuthController.java new file mode 100644 index 00000000..021d001b --- /dev/null +++ b/retail-managment-system/rms-api/src/main/java/com/rms/security/AuthController.java @@ -0,0 +1,40 @@ +package com.rms.security; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@RestController +@RequestMapping("/auth") +@AllArgsConstructor +@Tag(name = "Authentication", description = "REST API for Authentication") +@Log4j2 +public class AuthController { + + private AuthHandler authHandler; + + @PostMapping(value = "/login") + public ResponseEntity login(@Valid @RequestBody LoginRequestDto loginRequest) { + //TODO:add encryption/decryption + return authHandler.login(loginRequest); + } + + + @PostMapping(value = "/refresh") + public ResponseEntity refreshToken(@RequestBody RefreshTokenRequestDto refreshToken) { + return authHandler.refresh(refreshToken); + } + + @GetMapping(value = "/user-info") + @PreAuthorize("isAuthenticated()") + public ResponseEntity getUserInfo(@AuthenticationPrincipal UserDetails userDetails) { + return authHandler.getUserInfo(userDetails); + } +} diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/security/AuthHandler.java b/retail-managment-system/rms-api/src/main/java/com/rms/security/AuthHandler.java new file mode 100644 index 00000000..67b0ae8c --- /dev/null +++ b/retail-managment-system/rms-api/src/main/java/com/rms/security/AuthHandler.java @@ -0,0 +1,72 @@ +package com.rms.security; +import com.rms.rest.handler.UserHandler; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +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.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import java.util.Set; + +@Component +@RequiredArgsConstructor +@Log4j2 +public class AuthHandler { + + private final AuthenticationManager authenticationManager; + + private final TokenProvider tokenProvider; + @Value("${app.jwt.token.expiration-in-ms}") + private Long tokenExpirationMillis; + + @Value("${app.jwt.refresh.expiration-in-ms}") + private Long refreshTokenExpirationMillis; + + + @Autowired + private UserHandler userHandler; + + + + public ResponseEntity login(LoginRequestDto loginRequest) { + log.info("start login for user {}", loginRequest.getUsername()); + Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); + SecurityContextHolder.getContext().setAuthentication(authentication); + HttpHeaders responseHeaders = new HttpHeaders(); + String accessToken = tokenProvider.generateAccessToken(loginRequest.getUsername()); + String refreshToken = tokenProvider.generateRefreshToken(loginRequest.getUsername()); + return ResponseEntity.ok().body(new LoginResponseDto(accessToken, refreshToken)); + } + + + public ResponseEntity refresh(RefreshTokenRequestDto dto) { + Boolean refreshTokenValid = tokenProvider.validateToken(dto.getRefreshToken()); + if (!refreshTokenValid) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + + String currentUsername = tokenProvider.getUsernameFromToken(dto.getRefreshToken()); + + String newAccessToken = tokenProvider.generateAccessToken(currentUsername); + HttpHeaders responseHeaders = new HttpHeaders(); + + return ResponseEntity.ok().body(new LoginResponseDto(newAccessToken, dto.getRefreshToken())); + } + + + public ResponseEntity getUserInfo(UserDetails userDetails) { + UserDto userDto = UserDto.builder().username(userDetails.getUsername()).authorities((Set) userDetails.getAuthorities()).build(); + + return ResponseEntity.ok(userDto); + } + +} \ No newline at end of file diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/security/CustomUserDetailsService.java b/retail-managment-system/rms-api/src/main/java/com/rms/security/CustomUserDetailsService.java new file mode 100644 index 00000000..296411a8 --- /dev/null +++ b/retail-managment-system/rms-api/src/main/java/com/rms/security/CustomUserDetailsService.java @@ -0,0 +1,51 @@ +package com.rms.security; +import com.rms.domain.security.Role; +import com.rms.domain.security.User; +import com.rms.repository.UserRepository; +import com.rms.service.UserRoleService; +import lombok.AllArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +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 java.util.List; +import java.util.stream.Collectors; + +@Service +@AllArgsConstructor +@Log4j2 +public class CustomUserDetailsService implements UserDetailsService { + + private UserRepository userRepository; + private UserRoleService userRoleService; + + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + User user = userRepository.findByUserName(username).orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username)); + List roles= userRoleService.getRoleByUsername(username); + if (roles.isEmpty()){ + return org.springframework.security.core.userdetails.User + .withUsername(username) + .password(user.getPassword()) + .disabled(true) + .build(); + } + List authorities = roles.stream() + .map(role -> new SimpleGrantedAuthority(role.getCode())) + .collect(Collectors.toList()); + return org.springframework.security.core.userdetails.User + .withUsername(username) + .password(user.getPassword()) + .authorities(authorities) + .accountExpired(false) + .accountLocked(false) + .credentialsExpired(false) + .disabled(false) + .build(); + } +} diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/security/LoginRequestDto.java b/retail-managment-system/rms-api/src/main/java/com/rms/security/LoginRequestDto.java new file mode 100644 index 00000000..d6e78da7 --- /dev/null +++ b/retail-managment-system/rms-api/src/main/java/com/rms/security/LoginRequestDto.java @@ -0,0 +1,19 @@ +package com.rms.security; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Data +public class LoginRequestDto { + + @NotBlank(message = "username is mandatory") + @Size(max = 100, message = "username's max length allowed is 100 characters") + private String username; + + @NotBlank(message = "password is mandatory") + @Size(max = 100, message = "password's max length allowed is 100 characters") + private String password; + +} diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/security/LoginResponseDto.java b/retail-managment-system/rms-api/src/main/java/com/rms/security/LoginResponseDto.java new file mode 100644 index 00000000..29ad84ac --- /dev/null +++ b/retail-managment-system/rms-api/src/main/java/com/rms/security/LoginResponseDto.java @@ -0,0 +1,17 @@ +package com.rms.security; + +import lombok.Data; + +@Data +public class LoginResponseDto { + private String accessToken; + private String refreshToken; + + public LoginResponseDto(String accessToken, String refreshToken) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + } + + public LoginResponseDto() { + } +} diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/security/RefreshTokenRequestDto.java b/retail-managment-system/rms-api/src/main/java/com/rms/security/RefreshTokenRequestDto.java new file mode 100644 index 00000000..ce3c14fa --- /dev/null +++ b/retail-managment-system/rms-api/src/main/java/com/rms/security/RefreshTokenRequestDto.java @@ -0,0 +1,8 @@ +package com.rms.security; + +import lombok.Data; + +@Data +public class RefreshTokenRequestDto { + private String refreshToken; +} diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/security/RestAuthenticationEntryPoint.java b/retail-managment-system/rms-api/src/main/java/com/rms/security/RestAuthenticationEntryPoint.java new file mode 100644 index 00000000..cfca1c0b --- /dev/null +++ b/retail-managment-system/rms-api/src/main/java/com/rms/security/RestAuthenticationEntryPoint.java @@ -0,0 +1,27 @@ +package com.rms.security; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +@Component +public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { + private static final Logger logger = LoggerFactory.getLogger(RestAuthenticationEntryPoint.class); + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authException) throws IOException, ServletException { + logger.error("Unauthorized error: {}", authException.getMessage()); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized :D :D :D "); + } + +} + diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/security/TokenAuthenticationFilter.java b/retail-managment-system/rms-api/src/main/java/com/rms/security/TokenAuthenticationFilter.java new file mode 100644 index 00000000..55594857 --- /dev/null +++ b/retail-managment-system/rms-api/src/main/java/com/rms/security/TokenAuthenticationFilter.java @@ -0,0 +1,54 @@ +package com.rms.security; +import lombok.AllArgsConstructor; +import lombok.extern.log4j.Log4j2; +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.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +@AllArgsConstructor +@Log4j2 +public class TokenAuthenticationFilter extends OncePerRequestFilter { + @Autowired + private TokenProvider tokenProvider; + @Autowired + private UserDetailsService customUserDetailsService; + public TokenAuthenticationFilter() { + } + @Override + protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { + try { + String jwt = getJwtFromRequest(httpServletRequest); + if (tokenProvider.validateToken(jwt)) { + String username = tokenProvider.getUsernameFromToken(jwt); + UserDetails userDetails = customUserDetailsService.loadUserByUsername(username); + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + private String getJwtFromRequest(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + String accessToken = bearerToken.substring(7); + if (accessToken == null) return null; + return accessToken; + } + return null; + } + +} diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/security/TokenProvider.java b/retail-managment-system/rms-api/src/main/java/com/rms/security/TokenProvider.java new file mode 100644 index 00000000..d585a97f --- /dev/null +++ b/retail-managment-system/rms-api/src/main/java/com/rms/security/TokenProvider.java @@ -0,0 +1,80 @@ +package com.rms.security; + + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.security.Key; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; + +@Component +public class TokenProvider { + + private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); + @Value("${app.jwt.token.expiration-in-ms}") + private Long tokenExpirationMillis; + @Value("${app.jwt.refresh.expiration-in-ms}") + private Long refreshTokenExpirationMillis; + + public String generateAccessToken(String subject) { + Date now = new Date(); + Long duration = now.getTime() + tokenExpirationMillis; + Date expiryDate = new Date(duration); + + String token = Jwts.builder() + .setSubject(subject) + .setIssuedAt(now) + .setExpiration(expiryDate) + .signWith(key) + .compact(); + return token; + } + + public String generateRefreshToken(String subject) { + Date now = new Date(); + Long duration = now.getTime() + refreshTokenExpirationMillis; + Date expiryDate = new Date(duration); + String token = Jwts.builder() + .setSubject(subject) + .setIssuedAt(now) + .setExpiration(expiryDate) + .signWith(key) + .compact(); + return token; + } + + public String getUsernameFromToken(String token) { + Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); + return claims.getSubject(); + } + + public LocalDateTime getExpiryDateFromToken(String token) { + Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); + return LocalDateTime.ofInstant(claims.getExpiration().toInstant(), ZoneId.systemDefault()); + } + + public boolean validateToken(String token) { + try { + if (!StringUtils.hasText(token)) + return false; + Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); + return true; + } catch (SignatureException ex) { + ex.printStackTrace(); + } catch (MalformedJwtException ex) { + ex.printStackTrace(); + } catch (ExpiredJwtException ex) { + ex.printStackTrace(); + } catch (UnsupportedJwtException ex) { + ex.printStackTrace(); + } catch (IllegalArgumentException ex) { + ex.printStackTrace(); + } + return false; + } +} diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/security/UserDto.java b/retail-managment-system/rms-api/src/main/java/com/rms/security/UserDto.java new file mode 100644 index 00000000..e61f53ad --- /dev/null +++ b/retail-managment-system/rms-api/src/main/java/com/rms/security/UserDto.java @@ -0,0 +1,17 @@ +package com.rms.security; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Set; + + +@Builder +@Getter +public class UserDto { + + private String username; + private Set authorities; + +} diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/service/UserRoleService.java b/retail-managment-system/rms-api/src/main/java/com/rms/service/UserRoleService.java index cd3ce455..83f7748e 100644 --- a/retail-managment-system/rms-api/src/main/java/com/rms/service/UserRoleService.java +++ b/retail-managment-system/rms-api/src/main/java/com/rms/service/UserRoleService.java @@ -7,6 +7,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; + +import java.util.List; import java.util.Optional; @Service @@ -37,5 +39,8 @@ public Optional getRoleIdByUserIdAndRoleId(Integer userId,Integer roleI public Page getByUserId(Integer userId ,Integer page , Integer size){ return userRoleRepository.findUserRoleByUserId(userId , PageRequest.of(page,size)); } + public List getRoleByUsername(String username){ + return userRoleRepository.findByUserName(username); + } } diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/service/UserService.java b/retail-managment-system/rms-api/src/main/java/com/rms/service/UserService.java index 273d925a..9671c017 100644 --- a/retail-managment-system/rms-api/src/main/java/com/rms/service/UserService.java +++ b/retail-managment-system/rms-api/src/main/java/com/rms/service/UserService.java @@ -3,8 +3,10 @@ import com.rms.domain.security.User; import com.rms.repository.UserRepository; import lombok.AllArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.util.Optional; @@ -12,30 +14,44 @@ @Service @AllArgsConstructor public class UserService { + private UserRepository userRepository; - private UserRepository userRepository ; + @Autowired + private PasswordEncoder passwordEncoder; - public Page getAll(Integer page , Integer size) - { - return userRepository.findAll(PageRequest.of(page,size)); + public Page getAll(Integer page, Integer size) { + return userRepository.findAll(PageRequest.of(page, size)); } - public Optional getById(int id) - { - return userRepository.findById(id); + public User save(User user) { + user.setPassword(passwordEncoder.encode(user.getPassword())); + return userRepository.save(user); } - public User save (User user) - { - return userRepository.save(user); + public Optional getById(Integer id) { + return userRepository.findById(id); } - public Optional findUserName(String userName) { - return userRepository.findByUserName(userName); + public void update(User user) { + user.setPassword(passwordEncoder.encode(user.getPassword())); + userRepository.save(user); } - public void delete(User user) - { + public void delete(User user) { userRepository.delete(user); } + + + public Optional findUsername(String username) { + return userRepository.checkUsername(username); + } + + public Optional getByUserName(String username) { + return userRepository.findByUserName(username); + } + + + + + } diff --git a/retail-managment-system/rms-api/src/main/resources/application.yml b/retail-managment-system/rms-api/src/main/resources/application.yml index 5efb1cd9..f0e4e2ff 100644 --- a/retail-managment-system/rms-api/src/main/resources/application.yml +++ b/retail-managment-system/rms-api/src/main/resources/application.yml @@ -24,7 +24,7 @@ spring: ddl-auto: none properties: - hibernate: + 9+hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect # default_schema: rms_core show-sql: true @@ -45,4 +45,15 @@ spring: default-schema: public - +logging: + level: + org: + hibernate: + SQL: INFO + type: INFO +app: + jwt: + token: + expiration-in-ms: 86400000 + refresh: + expiration-in-ms: 108000000 \ No newline at end of file From 48d817801c5d375f530e1ca5a47a5a1901345f02 Mon Sep 17 00:00:00 2001 From: Hemmat Date: Sun, 19 Mar 2023 17:27:09 +0200 Subject: [PATCH 2/2] #136_add roles --- .../src/main/java/com/rms/config/WebSecurityConfig.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/retail-managment-system/rms-api/src/main/java/com/rms/config/WebSecurityConfig.java b/retail-managment-system/rms-api/src/main/java/com/rms/config/WebSecurityConfig.java index 80144831..75de6d8a 100644 --- a/retail-managment-system/rms-api/src/main/java/com/rms/config/WebSecurityConfig.java +++ b/retail-managment-system/rms-api/src/main/java/com/rms/config/WebSecurityConfig.java @@ -63,6 +63,10 @@ protected void configure(HttpSecurity http) throws Exception { .authenticationEntryPoint(restAuthenticationEntryPoint); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.authorizeRequests().antMatchers("/auth/**").permitAll(); + http.authorizeRequests().antMatchers("/customer/**","/installment/**", + "/investor/**","/employee/**","/profits/**" + ,"/role/**","/store/**","/supplier/**", + "/transaction/**","/user/**","/user-role/**").hasRole("ADMIN"); //for swagger http.authorizeRequests().antMatchers("/v3/api-docs/*", "/v3/api-docs", "/swagger-ui.html", "/swagger-ui/*").permitAll(); http.authorizeRequests().anyRequest().authenticated();