Skip to content

Commit

Permalink
Add SpaWebFilter for JWT
Browse files Browse the repository at this point in the history
  • Loading branch information
qmonmert committed Feb 23, 2025
1 parent d00c210 commit e2ace4e
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,13 @@ public JHipsterModule buildModule(JHipsterModuleProperties properties) {
.addTemplate("JWTFilter.java")
.addTemplate("JwtReader.java")
.addTemplate("SecurityConfiguration.java")
.addTemplate("SpaWebFilter.java")
.and()
.add(TEST_SOURCE.append(APPLICATION).template("AuthenticatedUserTest.java"), testDestination.append(APPLICATION).append("AuthenticatedUserTest.java"))
.batch(TEST_SOURCE.append(PRIMARY), testDestination.append(PRIMARY))
.addTemplate("JWTFilterTest.java")
.addTemplate("JwtReaderTest.java")
.addTemplate("SpaWebFilterIT.java")
.and()
.and()
.springMainProperties()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ 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.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.web.filter.CorsFilter;
Expand Down Expand Up @@ -55,6 +56,7 @@ class SecurityConfiguration {
http
.csrf(csrf -> csrf.disable())
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new SpaWebFilter(), BasicAuthenticationFilter.class)
.headers(headers -> headers
.contentSecurityPolicy(csp -> csp.policyDirectives(properties.getContentSecurityPolicy()))
.frameOptions(FrameOptionsConfig::deny)
Expand All @@ -67,9 +69,12 @@ class SecurityConfiguration {
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authz -> authz
.requestMatchers(antMatcher(HttpMethod.OPTIONS, "/**")).permitAll()
.requestMatchers(antMatcher("/"), antMatcher("/index.html"), antMatcher("/*.css"), antMatcher("/*.js")).permitAll()
.requestMatchers(antMatcher("/app/**")).permitAll()
.requestMatchers(antMatcher("/assets/**")).permitAll()
.requestMatchers(antMatcher("/i18n/**")).permitAll()
.requestMatchers(antMatcher("/content/**")).permitAll()
.requestMatchers(antMatcher("/style/**")).permitAll()
.requestMatchers(antMatcher("/swagger-ui/**")).permitAll()
.requestMatchers(antMatcher("/swagger-ui.html")).permitAll()
.requestMatchers(antMatcher("/v3/api-docs/**")).permitAll()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package {{packageName}}.shared.authentication.infrastructure.primary;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.web.filter.OncePerRequestFilter;

public class SpaWebFilter extends OncePerRequestFilter {
/**
* Forwards any unmapped paths (except those containing a period) to the client {@code index.html}.
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Request URI includes the contextPath if any, removed it.
String path = request.getRequestURI().substring(request.getContextPath().length());
if (
!path.startsWith("/api") &&
!path.startsWith("/management") &&
!path.startsWith("/v3/api-docs") &&
!path.contains(".")
) {
request.getRequestDispatcher("/index.html").forward(request, response);
return;
}

filterChain.doFilter(request, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package {{packageName}}.shared.authentication.infrastructure.primary;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import {{packageName}}.IntegrationTest;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@AutoConfigureMockMvc
@WithMockUser
@IntegrationTest
class SpaWebFilterIT {
@Autowired
private MockMvc mockMvc;
@Test
void testFilterForwardsToIndex() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("/index.html"));
}

@Test
@WithMockUser(authorities = "ADMIN")
void testFilterDoesNotForwardToIndexForV3ApiDocs() throws Exception {
mockMvc.perform(get("/v3/api-docs"))
.andExpect(status().isOk())
.andExpect(forwardedUrl(null));
}

@Test
@WithMockUser(authorities = "ADMIN")
void testFilterDoesNotForwardToIndexForManagement() throws Exception {
mockMvc.perform(get("/management"))
.andExpect(status().isForbidden())
.andExpect(forwardedUrl(null));
}

@Test
void testFilterDoesNotForwardToIndexForDotFile() throws Exception {
mockMvc.perform(get("/file.js"))
.andExpect(status().isNotFound());
}

@Test
void getBackendEndpoint() throws Exception {
mockMvc.perform(get("/test"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("/index.html"));
}

@Test
void forwardUnmappedFirstLevelMapping() throws Exception {
mockMvc.perform(get("/first-level"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("/index.html"));
}

@Test
void forwardUnmappedSecondLevelMapping() throws Exception {
mockMvc.perform(get("/first-level/second-level"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("/index.html"));
}

@Test
void forwardUnmappedThirdLevelMapping() throws Exception {
mockMvc.perform(get("/first-level/second-level/third-level"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("/index.html"));
}

@Test
void forwardUnmappedDeepMapping() throws Exception {
mockMvc.perform(get("/1/2/3/4/5/6/7/8/9/10"))
.andExpect(forwardedUrl("/index.html"));
}

@Test
void getUnmappedFirstLevelFile() throws Exception {
mockMvc.perform(get("/foo.js"))
.andExpect(status().isNotFound());
}
}

0 comments on commit e2ace4e

Please sign in to comment.