diff --git a/spring-security-modules/spring-security-core-2/pom.xml b/spring-security-modules/spring-security-core-2/pom.xml
index ace629eef1..94940f2613 100644
--- a/spring-security-modules/spring-security-core-2/pom.xml
+++ b/spring-security-modules/spring-security-core-2/pom.xml
@@ -17,6 +17,7 @@
com.baeldung.authresolver.AuthResolverApplication
+ 0.12.5
@@ -61,6 +62,21 @@
org.springframework.security
spring-security-core
+
+ io.jsonwebtoken
+ jjwt-api
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ ${jjwt.version}
+
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/SpringJwtApplication.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/SpringJwtApplication.java
new file mode 100644
index 0000000000..611935e566
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/SpringJwtApplication.java
@@ -0,0 +1,14 @@
+package com.baeldung.jwtsignkey;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+
+@SpringBootApplication
+@EnableWebMvc
+public class SpringJwtApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(com.baeldung.jwtsignkey.SpringJwtApplication.class);
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/controller/JwtAuthController.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/controller/JwtAuthController.java
new file mode 100644
index 0000000000..37de1682db
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/controller/JwtAuthController.java
@@ -0,0 +1,77 @@
+package com.baeldung.jwtsignkey.controller;
+
+import com.baeldung.jwtsignkey.jwtconfig.JwtUtils;
+import com.baeldung.jwtsignkey.model.User;
+import com.baeldung.jwtsignkey.repository.UserRepository;
+import com.baeldung.jwtsignkey.response.JwtResponse;
+import com.baeldung.jwtsignkey.userservice.UserDetailsImpl;
+import com.baeldung.request.LoginRequest;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+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.security.crypto.password.PasswordEncoder;
+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 java.io.UnsupportedEncodingException;
+
+@RestController
+public class JwtAuthController {
+
+ @Autowired
+ AuthenticationManager authenticationManager;
+
+ @Autowired
+ PasswordEncoder encoder;
+
+ @Autowired
+ JwtUtils jwtUtils;
+
+ @Autowired
+ UserRepository userRepository;
+
+ @PostMapping("/signup")
+ public ResponseEntity> registerUser(@RequestBody User signUpRequest, HttpServletRequest request) throws UnsupportedEncodingException {
+
+ if (userRepository.existsByUsername(signUpRequest.getUsername())) {
+ return ResponseEntity.badRequest()
+ .body("Error: Username is already taken!");
+ }
+ User user = new User();
+ user.setUsername(signUpRequest.getUsername());
+ user.setPassword(encoder.encode(signUpRequest.getPassword()));
+
+ userRepository.save(user);
+
+ return ResponseEntity.ok(user);
+ }
+
+ @PostMapping("/signin")
+ public ResponseEntity> authenticateUser(@RequestBody LoginRequest loginRequest) {
+
+ Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
+
+ SecurityContextHolder.getContext()
+ .setAuthentication(authentication);
+ UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
+
+ String jwt = jwtUtils.generateJwtToken(authentication);
+
+ return ResponseEntity.ok(new JwtResponse(jwt, userDetails.getUsername()));
+
+ }
+
+ @RequestMapping("/user-dashboard")
+ @PreAuthorize("isAuthenticated()")
+ public String dashboard() {
+ return "My Dashboard";
+ }
+
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthEntryPointJwt.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthEntryPointJwt.java
new file mode 100644
index 0000000000..1cc64f3550
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthEntryPointJwt.java
@@ -0,0 +1,39 @@
+package com.baeldung.jwtsignkey.jwtconfig;
+
+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 {
+ logger.error("Unauthorized error: {}", authException.getMessage());
+
+ response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+
+ final Map 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);
+ }
+}
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthTokenFilter.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthTokenFilter.java
new file mode 100644
index 0000000000..a149c71121
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthTokenFilter.java
@@ -0,0 +1,59 @@
+package com.baeldung.jwtsignkey.jwtconfig;
+
+import com.baeldung.jwtsignkey.userservice.UserDetailsServiceImpl;
+import jakarta.servlet.FilterChain;
+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.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.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+public class AuthTokenFilter extends OncePerRequestFilter {
+ @Autowired
+ private JwtUtils jwtUtils;
+
+ @Autowired
+ private UserDetailsServiceImpl userDetailsService;
+
+ private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);
+
+ @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 headerAuth = request.getHeader("Authorization");
+
+ if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
+ return headerAuth.substring(7, headerAuth.length());
+ }
+
+ return null;
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/JwtUtils.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/JwtUtils.java
new file mode 100644
index 0000000000..5fbd4c6de3
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/JwtUtils.java
@@ -0,0 +1,81 @@
+package com.baeldung.jwtsignkey.jwtconfig;
+
+import com.baeldung.jwtsignkey.userservice.UserDetailsImpl;
+
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.UnsupportedJwtException;
+import io.jsonwebtoken.io.Decoders;
+import io.jsonwebtoken.security.Keys;
+import io.jsonwebtoken.security.SignatureException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import java.util.Date;
+
+@Component
+public class JwtUtils {
+ private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
+
+ @Value("${baeldung.app.jwtSecret}")
+ private String jwtSecret;
+
+ @Value("${baeldung.app.jwtExpirationMs}")
+ private int jwtExpirationMs;
+
+ public String generateJwtToken(Authentication authentication) {
+
+ UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
+
+ return Jwts.builder()
+ .subject((userPrincipal.getUsername()))
+ .issuedAt(new Date())
+ .expiration(new Date((new Date()).getTime() + jwtExpirationMs))
+ .signWith(getSigningKey())
+ .compact();
+
+ }
+
+ private SecretKey getSigningKey() {
+ byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
+ return Keys.hmacShaKeyFor(keyBytes);
+ }
+
+ public String getUserNameFromJwtToken(String token) {
+ return Jwts.parser()
+ .verifyWith(getSigningKey())
+ .build()
+ .parseSignedClaims(token)
+ .getPayload()
+ .getSubject();
+
+ }
+
+ public boolean validateJwtToken(String authToken) {
+ try {
+ Jwts.parser()
+ .verifyWith(getSigningKey())
+ .build()
+ .parseSignedClaims(authToken);
+ return true;
+ } catch (SignatureException e) {
+ logger.error("Invalid JWT signature: {}", e.getMessage());
+ } 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;
+ }
+
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/model/User.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/model/User.java
new file mode 100644
index 0000000000..c7acda047a
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/model/User.java
@@ -0,0 +1,45 @@
+package com.baeldung.jwtsignkey.model;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+
+@Entity
+public class User {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ private String username;
+
+ private String password;
+
+ public User() {
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/repository/UserRepository.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/repository/UserRepository.java
new file mode 100644
index 0000000000..886338242c
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/repository/UserRepository.java
@@ -0,0 +1,14 @@
+package com.baeldung.jwtsignkey.repository;
+
+import com.baeldung.jwtsignkey.model.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface UserRepository extends JpaRepository {
+
+ Boolean existsByUsername(String username);
+
+ Optional findByUsername(String username);
+
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/response/JwtResponse.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/response/JwtResponse.java
new file mode 100644
index 0000000000..f5b3148c42
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/response/JwtResponse.java
@@ -0,0 +1,37 @@
+package com.baeldung.jwtsignkey.response;
+
+public class JwtResponse {
+ private String token;
+ private String type = "Bearer";
+
+ private String username;
+
+ public JwtResponse(String accessToken, String username) {
+ this.token = accessToken;
+ this.username = username;
+ }
+
+ public String getAccessToken() {
+ return token;
+ }
+
+ public void setAccessToken(String accessToken) {
+ this.token = accessToken;
+ }
+
+ public String getTokenType() {
+ return type;
+ }
+
+ public void setTokenType(String tokenType) {
+ this.type = tokenType;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/securityconfig/SecurityConfiguration.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/securityconfig/SecurityConfiguration.java
new file mode 100644
index 0000000000..30e8c552c9
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/securityconfig/SecurityConfiguration.java
@@ -0,0 +1,79 @@
+package com.baeldung.jwtsignkey.securityconfig;
+
+import com.baeldung.jwtsignkey.jwtconfig.AuthEntryPointJwt;
+import com.baeldung.jwtsignkey.jwtconfig.AuthTokenFilter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
+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.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.core.userdetails.UserDetailsService;
+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;
+import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
+import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
+
+import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
+
+@Configuration
+@EnableWebSecurity
+@EnableMethodSecurity
+public class SecurityConfiguration {
+
+ @Autowired
+ UserDetailsService userDetailsService;
+ @Autowired
+ private AuthEntryPointJwt unauthorizedHandler;
+
+ private static final String[] WHITE_LIST_URL = { "/h2-console/**", "/signin", "/signup", "/user-dashboard" };
+
+ @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 PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
+ return authConfig.getAuthenticationManager();
+ }
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
+ http.csrf(AbstractHttpConfigurer::disable)
+ .cors(AbstractHttpConfigurer::disable)
+ .authorizeHttpRequests(req -> req.requestMatchers(WHITE_LIST_URL)
+ .permitAll()
+ .anyRequest()
+ .authenticated())
+ .exceptionHandling(ex -> ex.authenticationEntryPoint(unauthorizedHandler))
+ .sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
+ .authenticationProvider(authenticationProvider())
+ .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
+
+ return http.build();
+ }
+
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsImpl.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsImpl.java
new file mode 100644
index 0000000000..a651a1088a
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsImpl.java
@@ -0,0 +1,85 @@
+package com.baeldung.jwtsignkey.userservice;
+
+import com.baeldung.jwtsignkey.model.User;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+import java.util.Objects;
+
+public class UserDetailsImpl implements UserDetails {
+ private static final long serialVersionUID = 1L;
+
+ private Long id;
+
+ private String username;
+
+ @JsonIgnore
+ private String password;
+
+ public UserDetailsImpl(Long id, String username, String password) {
+ this.id = id;
+ this.username = username;
+ this.password = password;
+
+ }
+
+ public static UserDetailsImpl build(User user) {
+
+ return new UserDetailsImpl(user.getId(), user.getUsername(), user.getPassword());
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public static long getSerialversionuid() {
+ return serialVersionUID;
+ }
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return null;
+ }
+
+ @Override
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String getUsername() {
+ return username;
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ UserDetailsImpl user = (UserDetailsImpl) o;
+ return Objects.equals(id, user.id);
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsServiceImpl.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsServiceImpl.java
new file mode 100644
index 0000000000..c544f748cd
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsServiceImpl.java
@@ -0,0 +1,28 @@
+package com.baeldung.jwtsignkey.userservice;
+
+import com.baeldung.jwtsignkey.model.User;
+import com.baeldung.jwtsignkey.repository.UserRepository;
+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 org.springframework.transaction.annotation.Transactional;
+
+;
+
+@Service
+public class UserDetailsServiceImpl implements UserDetailsService {
+
+ @Autowired
+ UserRepository userRepository;
+
+ @Override
+ @Transactional
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ User user = userRepository.findByUsername(username)
+ .orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
+
+ return UserDetailsImpl.build(user);
+ }
+}
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/request/LoginRequest.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/request/LoginRequest.java
new file mode 100644
index 0000000000..24555136c2
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/request/LoginRequest.java
@@ -0,0 +1,24 @@
+package com.baeldung.request;
+
+public class LoginRequest {
+
+ private String username;
+
+ private String password;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/resources/application.properties b/spring-security-modules/spring-security-core-2/src/main/resources/application.properties
index 9d154c9cc0..6005cdd2d2 100644
--- a/spring-security-modules/spring-security-core-2/src/main/resources/application.properties
+++ b/spring-security-modules/spring-security-core-2/src/main/resources/application.properties
@@ -1 +1,2 @@
-spring.thymeleaf.prefix=classpath:/templates/
\ No newline at end of file
+baeldung.app.jwtSecret= 404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970
+baeldung.app.jwtExpirationMs= 8640000
diff --git a/spring-security-modules/spring-security-core-2/src/test/java/com/baeldung/jwtsecuritykey/JwtSignKeyIntegrationTest.java b/spring-security-modules/spring-security-core-2/src/test/java/com/baeldung/jwtsecuritykey/JwtSignKeyIntegrationTest.java
new file mode 100644
index 0000000000..73d1142ceb
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/test/java/com/baeldung/jwtsecuritykey/JwtSignKeyIntegrationTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.jwtsecuritykey;
+
+import com.baeldung.jwtsignkey.SpringJwtApplication;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@SpringBootTest(classes = SpringJwtApplication.class)
+public class JwtSignKeyIntegrationTest {
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mvc;
+
+ @BeforeEach
+ private void setup() {
+ mvc = MockMvcBuilders.webAppContextSetup(context)
+ .apply(springSecurity())
+ .build();
+ }
+
+
+ @Test
+ public void givenAnonymous_whenAccessUserDashboard_thenForbidden() throws Exception {
+ mvc.perform(get("/user-dashboard"))
+ .andExpect(status().isUnauthorized());
+ }
+
+
+}
+