Add Security & Repository Package (JWT, Repository, Enums)
This commit is contained in:
		| @@ -0,0 +1,68 @@ | ||||
| package com.hakimfauzi23.boilerplatespringsecurity.modules.auth; | ||||
|  | ||||
| import com.hakimfauzi23.boilerplatespringsecurity.modules.auth.jwt.AuthEntryPointJwt; | ||||
| import com.hakimfauzi23.boilerplatespringsecurity.modules.auth.jwt.AuthTokenFilter; | ||||
| import com.hakimfauzi23.boilerplatespringsecurity.modules.auth.service.UserDetailsServiceImpl; | ||||
| 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.method.configuration.EnableMethodSecurity; | ||||
| import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||||
| 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.SecurityFilterChain; | ||||
| import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||||
|  | ||||
| @Configuration | ||||
| @EnableMethodSecurity | ||||
| public class WebSecurityConfig { | ||||
|  | ||||
|     @Autowired | ||||
|     UserDetailsServiceImpl userDetailsService; | ||||
|  | ||||
|     @Autowired | ||||
|     private AuthEntryPointJwt unauthorizedHandler; | ||||
|  | ||||
|     @Bean | ||||
|     public AuthTokenFilter authenticationJwtTokenFilter() { | ||||
|         return new AuthTokenFilter(); | ||||
|     } | ||||
|  | ||||
|     @Bean | ||||
|     public DaoAuthenticationProvider authenticationProvider() { | ||||
|         DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); | ||||
|  | ||||
|         authProvider.setUserDetailsService(userDetailsService); | ||||
|         authProvider.setPasswordEncoder(passwordEncoder()); | ||||
|         return authProvider; | ||||
|     } | ||||
|  | ||||
|     @Bean | ||||
|     public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { | ||||
|         return authConfig.getAuthenticationManager(); | ||||
|     } | ||||
|  | ||||
|     @Bean | ||||
|     public PasswordEncoder passwordEncoder() { | ||||
|         return new BCryptPasswordEncoder(); | ||||
|     } | ||||
|  | ||||
|     @Bean | ||||
|     public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { | ||||
|         httpSecurity.csrf(csrf -> csrf.disable()) | ||||
|                 .exceptionHandling(ex -> ex.authenticationEntryPoint(unauthorizedHandler)) | ||||
|                 .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) | ||||
|                 .authorizeHttpRequests(auth -> | ||||
|                         auth.requestMatchers("/api/auth/**").permitAll() | ||||
|                                 .requestMatchers("api/test/**").permitAll() | ||||
|                                 .anyRequest().authenticated()); | ||||
|  | ||||
|         httpSecurity.authenticationProvider(authenticationProvider()); | ||||
|         httpSecurity.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); | ||||
|         return httpSecurity.build(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,38 @@ | ||||
| package com.hakimfauzi23.boilerplatespringsecurity.modules.auth.jwt; | ||||
|  | ||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||
| import jakarta.servlet.ServletException; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.security.core.AuthenticationException; | ||||
| import org.springframework.security.web.AuthenticationEntryPoint; | ||||
| import org.springframework.stereotype.Component; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| @Component | ||||
| public class AuthEntryPointJwt implements AuthenticationEntryPoint { | ||||
|  | ||||
|     private static final Logger LOGGER = LoggerFactory.getLogger(AuthEntryPointJwt.class); | ||||
|  | ||||
|     @Override | ||||
|     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { | ||||
|  | ||||
|         response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||||
|         response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | ||||
|  | ||||
|         final Map<String, Object> body = new HashMap<>(); | ||||
|         body.put("status", HttpServletResponse.SC_UNAUTHORIZED); | ||||
|         body.put("error", "Unauthorized"); | ||||
|         body.put("message", authException.getMessage()); | ||||
|         body.put("path", request.getServletPath()); | ||||
|  | ||||
|         final ObjectMapper mapper = new ObjectMapper(); | ||||
|         mapper.writeValue(response.getOutputStream(), body); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,55 @@ | ||||
| package com.hakimfauzi23.boilerplatespringsecurity.modules.auth.jwt; | ||||
|  | ||||
| import com.hakimfauzi23.boilerplatespringsecurity.modules.auth.service.UserDetailsServiceImpl; | ||||
| import jakarta.servlet.FilterChain; | ||||
| import jakarta.servlet.ServletException; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| 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.web.authentication.WebAuthenticationDetailsSource; | ||||
| import org.springframework.web.filter.OncePerRequestFilter; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| public class AuthTokenFilter extends OncePerRequestFilter { | ||||
|  | ||||
|     @Autowired | ||||
|     private JwtUtils jwtUtils; | ||||
|  | ||||
|     @Autowired | ||||
|     private UserDetailsServiceImpl userDetailsService; | ||||
|  | ||||
|     @Override | ||||
|     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | ||||
|             throws ServletException, IOException { | ||||
|         try { | ||||
|             String jwt = parseJwt(request); | ||||
|             if (jwt != null && jwtUtils.validateJwtToken(jwt)) { | ||||
|                 String username = jwtUtils.getUserNameFromJwtToken(jwt); | ||||
|  | ||||
|                 UserDetails userDetails = userDetailsService.loadUserByUsername(username); | ||||
|  | ||||
|                 UsernamePasswordAuthenticationToken authentication = | ||||
|                         new UsernamePasswordAuthenticationToken(userDetails, | ||||
|                                 null, | ||||
|                                 userDetails.getAuthorities()); | ||||
|  | ||||
|                 authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | ||||
|  | ||||
|                 SecurityContextHolder.getContext().setAuthentication(authentication); | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             logger.error("Cannot set user authentication: {}", e); | ||||
|         } | ||||
|  | ||||
|         filterChain.doFilter(request, response); | ||||
|     } | ||||
|  | ||||
|     private String parseJwt(HttpServletRequest request) { | ||||
|         String jwt = jwtUtils.getJwtFromCookies(request); | ||||
|         return jwt; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,87 @@ | ||||
| package com.hakimfauzi23.boilerplatespringsecurity.modules.auth.jwt; | ||||
|  | ||||
| import com.hakimfauzi23.boilerplatespringsecurity.modules.auth.service.UserDetailsImpl; | ||||
| import io.jsonwebtoken.*; | ||||
| import io.jsonwebtoken.io.Decoders; | ||||
| import io.jsonwebtoken.security.Keys; | ||||
| import jakarta.servlet.http.Cookie; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.http.ResponseCookie; | ||||
| import org.springframework.stereotype.Component; | ||||
| import org.springframework.web.util.WebUtils; | ||||
|  | ||||
| import java.security.Key; | ||||
| import java.util.Date; | ||||
|  | ||||
| @Component | ||||
| public class JwtUtils { | ||||
|  | ||||
|     private static final Logger LOGGER = LoggerFactory.getLogger(JwtUtils.class); | ||||
|  | ||||
|     @Value("${spring.app.jwtSecret}") | ||||
|     private String jwtSecret; | ||||
|  | ||||
|     @Value("${spring.app.jwtExpirationMs}") | ||||
|     private int jwtExpirationMs; | ||||
|  | ||||
|     @Value("${spring.app.jwtCookieName}") | ||||
|     private String jwtCookie; | ||||
|  | ||||
|     public String getJwtFromCookies(HttpServletRequest request) { | ||||
|         Cookie cookie = WebUtils.getCookie(request, jwtCookie); | ||||
|         if (cookie != null) { | ||||
|             return cookie.getValue(); | ||||
|         } else { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public ResponseCookie generateJwtCookie(UserDetailsImpl userDetails) { | ||||
|         String jwt = generateTokenFromUsername(userDetails.getUsername()); | ||||
|         ResponseCookie cookie = ResponseCookie.from(jwtCookie, jwt).path("/api").maxAge(24 * 60 * 60).httpOnly(true).build(); | ||||
|         return cookie; | ||||
|     } | ||||
|  | ||||
|     public ResponseCookie getCleanJwtCookie() { | ||||
|         ResponseCookie cookie = ResponseCookie.from(jwtCookie, null).path("/api").build(); | ||||
|         return cookie; | ||||
|     } | ||||
|  | ||||
|     public String getUserNameFromJwtToken(String token) { | ||||
|         return Jwts.parserBuilder().setSigningKey(key()).build() | ||||
|                 .parseClaimsJws(token).getBody().getSubject(); | ||||
|     } | ||||
|  | ||||
|     private Key key() { | ||||
|         return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret)); | ||||
|     } | ||||
|  | ||||
|     public boolean validateJwtToken(String authToken) { | ||||
|         try { | ||||
|             Jwts.parserBuilder().setSigningKey(key()).build().parse(authToken); | ||||
|             return true; | ||||
|         } catch (MalformedJwtException e) { | ||||
|             LOGGER.error("Invalid JWT token: {}", e.getMessage()); | ||||
|         } catch (ExpiredJwtException e) { | ||||
|             LOGGER.error("JWT token is expired: {}", e.getMessage()); | ||||
|         } catch (UnsupportedJwtException e) { | ||||
|             LOGGER.error("JWT token is unsupported: {}", e.getMessage()); | ||||
|         } catch (IllegalArgumentException e) { | ||||
|             LOGGER.error("JWT claims string is empty: {}", e.getMessage()); | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public String generateTokenFromUsername(String username) { | ||||
|         return Jwts.builder() | ||||
|                 .setSubject(username) | ||||
|                 .setIssuedAt(new Date()) | ||||
|                 .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) | ||||
|                 .signWith(key(), SignatureAlgorithm.HS256) | ||||
|                 .compact(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,15 @@ | ||||
| package com.hakimfauzi23.boilerplatespringsecurity.modules.auth.repository; | ||||
|  | ||||
| import com.hakimfauzi23.boilerplatespringsecurity.modules.auth.data.ERole; | ||||
| import com.hakimfauzi23.boilerplatespringsecurity.modules.auth.data.Role; | ||||
| import org.springframework.data.jpa.repository.JpaRepository; | ||||
| import org.springframework.stereotype.Repository; | ||||
|  | ||||
| import java.util.Optional; | ||||
|  | ||||
| @Repository | ||||
| public interface RoleRepository extends JpaRepository<Role, Long> { | ||||
|  | ||||
|     Optional<Role> findByName(ERole name); | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| package com.hakimfauzi23.boilerplatespringsecurity.modules.auth.repository; | ||||
|  | ||||
| import com.hakimfauzi23.boilerplatespringsecurity.modules.auth.data.User; | ||||
| import org.springframework.data.jpa.repository.JpaRepository; | ||||
| import org.springframework.stereotype.Repository; | ||||
|  | ||||
| import java.util.Optional; | ||||
|  | ||||
| @Repository | ||||
| public interface UserRepository extends JpaRepository<User, Long> { | ||||
|  | ||||
|     Optional<User> findByUsername(String username); | ||||
|  | ||||
|     Boolean existsByUsername(String username); | ||||
|  | ||||
|     Boolean existsByEmail(String email); | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user