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 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()); + } + + +} +