contextPath fixes
This commit is contained in:
parent
3911be0177
commit
8acab77ae3
9 changed files with 88 additions and 28 deletions
|
@ -5,11 +5,13 @@ import java.io.IOException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||||
|
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
|
public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||||
|
@ -19,8 +21,32 @@ public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthent
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
|
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
|
||||||
String username = request.getParameter("username");
|
String username = request.getParameter("username");
|
||||||
loginAttemptService.loginSucceeded(username);
|
loginAttemptService.loginSucceeded(username);
|
||||||
super.onAuthenticationSuccess(request, response, authentication);
|
|
||||||
|
|
||||||
|
// Get the saved request
|
||||||
|
HttpSession session = request.getSession(false);
|
||||||
|
SavedRequest savedRequest = session != null ? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST") : null;
|
||||||
|
if (savedRequest != null && !isStaticResource(savedRequest)) {
|
||||||
|
// Redirect to the original destination
|
||||||
|
super.onAuthenticationSuccess(request, response, authentication);
|
||||||
|
} else {
|
||||||
|
// Redirect to the root URL (considering context path)
|
||||||
|
getRedirectStrategy().sendRedirect(request, response, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
//super.onAuthenticationSuccess(request, response, authentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isStaticResource(SavedRequest savedRequest) {
|
||||||
|
String requestURI = savedRequest.getRedirectUrl();
|
||||||
|
return requestURI.startsWith("/css/")
|
||||||
|
|| requestURI.startsWith("/js/")
|
||||||
|
|| requestURI.startsWith("/images/")
|
||||||
|
|| requestURI.startsWith("/public/")
|
||||||
|
|| requestURI.startsWith("/pdfjs/")
|
||||||
|
|| requestURI.endsWith(".svg");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
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.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
@ -15,12 +16,13 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
||||||
|
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
|
||||||
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity()
|
@EnableWebSecurity()
|
||||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
@EnableMethodSecurity
|
||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -41,9 +43,7 @@ public class SecurityConfiguration {
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserAuthenticationFilter userAuthenticationFilter;
|
private UserAuthenticationFilter userAuthenticationFilter;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private LoginAttemptService loginAttemptService;
|
private LoginAttemptService loginAttemptService;
|
||||||
|
@ -63,11 +63,13 @@ public class SecurityConfiguration {
|
||||||
http
|
http
|
||||||
.formLogin(formLogin -> formLogin
|
.formLogin(formLogin -> formLogin
|
||||||
.loginPage("/login")
|
.loginPage("/login")
|
||||||
.successHandler(customAuthenticationSuccessHandler)
|
.successHandler(new CustomAuthenticationSuccessHandler())
|
||||||
// .defaultSuccessUrl("/")
|
.defaultSuccessUrl("/")
|
||||||
.failureHandler(new CustomAuthenticationFailureHandler(loginAttemptService))
|
.failureHandler(new CustomAuthenticationFailureHandler(loginAttemptService))
|
||||||
.permitAll()
|
.permitAll()
|
||||||
)
|
).requestCache(requestCache -> requestCache
|
||||||
|
.requestCache(new NullRequestCache())
|
||||||
|
)
|
||||||
.logout(logout -> logout
|
.logout(logout -> logout
|
||||||
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
|
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
|
||||||
.logoutSuccessUrl("/login?logout=true")
|
.logoutSuccessUrl("/login?logout=true")
|
||||||
|
@ -79,8 +81,19 @@ public class SecurityConfiguration {
|
||||||
.tokenValiditySeconds(1209600) // 2 weeks
|
.tokenValiditySeconds(1209600) // 2 weeks
|
||||||
)
|
)
|
||||||
.authorizeHttpRequests(authz -> authz
|
.authorizeHttpRequests(authz -> authz
|
||||||
.requestMatchers(req -> req.getRequestURI().startsWith("/login") || req.getRequestURI().endsWith(".svg") || req.getRequestURI().startsWith("/register") || req.getRequestURI().startsWith("/error") || req.getRequestURI().startsWith("/images/") || req.getRequestURI().startsWith("/public/") || req.getRequestURI().startsWith("/css/") || req.getRequestURI().startsWith("/js/"))
|
.requestMatchers(req -> {
|
||||||
.permitAll()
|
String uri = req.getRequestURI();
|
||||||
|
String contextPath = req.getContextPath();
|
||||||
|
|
||||||
|
// Remove the context path from the URI
|
||||||
|
String trimmedUri = uri.startsWith(contextPath) ? uri.substring(contextPath.length()) : uri;
|
||||||
|
|
||||||
|
return trimmedUri.startsWith("/login") || trimmedUri.endsWith(".svg") ||
|
||||||
|
trimmedUri.startsWith("/register") || trimmedUri.startsWith("/error") ||
|
||||||
|
trimmedUri.startsWith("/images/") || trimmedUri.startsWith("/public/") ||
|
||||||
|
trimmedUri.startsWith("/css/") || trimmedUri.startsWith("/js/");
|
||||||
|
}
|
||||||
|
).permitAll()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.userDetailsService(userDetailsService)
|
.userDetailsService(userDetailsService)
|
||||||
|
|
|
@ -74,8 +74,10 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||||
// If we still don't have any authentication, deny the request
|
// If we still don't have any authentication, deny the request
|
||||||
if (authentication == null || !authentication.isAuthenticated()) {
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
String method = request.getMethod();
|
String method = request.getMethod();
|
||||||
if ("GET".equalsIgnoreCase(method) && !"/login".equals(requestURI)) {
|
String contextPath = request.getContextPath();
|
||||||
response.sendRedirect("/login"); // redirect to the login page
|
|
||||||
|
if ("GET".equalsIgnoreCase(method) && ! (contextPath + "/login").equals(requestURI)) {
|
||||||
|
response.sendRedirect(contextPath + "/login"); // redirect to the login page
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
|
@ -90,15 +92,17 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
|
String contextPath = request.getContextPath();
|
||||||
String[] permitAllPatterns = {
|
String[] permitAllPatterns = {
|
||||||
"/login",
|
contextPath + "/login",
|
||||||
"/register",
|
contextPath + "/register",
|
||||||
"/error",
|
contextPath + "/error",
|
||||||
"/images/",
|
contextPath + "/images/",
|
||||||
"/public/",
|
contextPath + "/public/",
|
||||||
"/css/",
|
contextPath + "/css/",
|
||||||
"/js/"
|
contextPath + "/js/",
|
||||||
|
contextPath + "/pdfjs/",
|
||||||
|
contextPath + "/site.webmanifest"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (String pattern : permitAllPatterns) {
|
for (String pattern : permitAllPatterns) {
|
||||||
|
|
|
@ -15,14 +15,22 @@ import org.springframework.web.client.RestTemplate;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import jakarta.servlet.ServletContext;
|
||||||
import stirling.software.SPDF.model.ApiEndpoint;
|
import stirling.software.SPDF.model.ApiEndpoint;
|
||||||
import stirling.software.SPDF.model.Role;
|
import stirling.software.SPDF.model.Role;
|
||||||
@Service
|
@Service
|
||||||
public class ApiDocService {
|
public class ApiDocService {
|
||||||
|
|
||||||
private final Map<String, ApiEndpoint> apiDocumentation = new HashMap<>();
|
private final Map<String, ApiEndpoint> apiDocumentation = new HashMap<>();
|
||||||
private final String apiDocsUrl = "http://localhost:8080/v1/api-docs"; // URL to your API documentation
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ServletContext servletContext;
|
||||||
|
|
||||||
|
private String getApiDocsUrl() {
|
||||||
|
String contextPath = servletContext.getContextPath();
|
||||||
|
return "http://localhost:8080" + contextPath + "/v1/api-docs";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Autowired(required=false)
|
@Autowired(required=false)
|
||||||
private UserServiceInterface userService;
|
private UserServiceInterface userService;
|
||||||
|
@ -44,7 +52,7 @@ public class ApiDocService {
|
||||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||||
|
|
||||||
RestTemplate restTemplate = new RestTemplate();
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
ResponseEntity<String> response = restTemplate.exchange(apiDocsUrl, HttpMethod.GET, entity, String.class);
|
ResponseEntity<String> response = restTemplate.exchange(getApiDocsUrl(), HttpMethod.GET, entity, String.class);
|
||||||
String apiDocsJson = response.getBody();
|
String apiDocsJson = response.getBody();
|
||||||
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
|
@ -50,6 +50,7 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.ServletContext;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.PipelineConfig;
|
import stirling.software.SPDF.model.PipelineConfig;
|
||||||
import stirling.software.SPDF.model.PipelineOperation;
|
import stirling.software.SPDF.model.PipelineOperation;
|
||||||
|
@ -117,6 +118,14 @@ public class PipelineController {
|
||||||
return userService.getApiKeyForUser(Role.INTERNAL_API_USER.getRoleId());
|
return userService.getApiKeyForUser(Role.INTERNAL_API_USER.getRoleId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ServletContext servletContext;
|
||||||
|
|
||||||
|
private String getBaseUrl() {
|
||||||
|
String contextPath = servletContext.getContextPath();
|
||||||
|
return "http://localhost:8080" + contextPath + "/";
|
||||||
|
}
|
||||||
|
|
||||||
private void handleDirectory(Path dir) throws Exception {
|
private void handleDirectory(Path dir) throws Exception {
|
||||||
logger.info("Handling directory: {}", dir);
|
logger.info("Handling directory: {}", dir);
|
||||||
|
|
||||||
|
@ -337,7 +346,7 @@ public class PipelineController {
|
||||||
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
|
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
|
||||||
|
|
||||||
RestTemplate restTemplate = new RestTemplate();
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
String url = "http://localhost:8080/" + operation;
|
String url = getBaseUrl() + operation;
|
||||||
|
|
||||||
ResponseEntity<byte[]> response = restTemplate.exchange(url, HttpMethod.POST, entity, byte[].class);
|
ResponseEntity<byte[]> response = restTemplate.exchange(url, HttpMethod.POST, entity, byte[].class);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#searchBar {
|
#searchBar {
|
||||||
background-image: url('/images/search.svg');
|
background-image: url('../images/search.svg');
|
||||||
background-position: 16px 16px;
|
background-position: 16px 16px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -130,7 +130,7 @@ document.getElementById('submitConfigBtn').addEventListener('click', function()
|
||||||
formData.append('json', pipelineConfigJson);
|
formData.append('json', pipelineConfigJson);
|
||||||
console.log("formData", formData);
|
console.log("formData", formData);
|
||||||
|
|
||||||
fetch('/api/v1/pipeline/handleData', {
|
fetch('api/v1/pipeline/handleData', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
|
|
|
@ -306,7 +306,7 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="mb-3 mt-4">
|
<div class="mb-3 mt-4">
|
||||||
<a href="/logout">
|
<a href="logout">
|
||||||
<button type="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</button>
|
<button type="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</button>
|
||||||
</a>
|
</a>
|
||||||
<a th:if="${role == 'ROLE_ADMIN'}" href="addUsers" target="_blank">
|
<a th:if="${role == 'ROLE_ADMIN'}" href="addUsers" target="_blank">
|
||||||
|
|
|
@ -293,7 +293,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a th:if="${@loginEnabled}" href="/logout">
|
<a th:if="${@loginEnabled}" href="logout">
|
||||||
<button type="button" class="btn btn-danger" th:text="#{settings.signOut}">Sign Out</button>
|
<button type="button" class="btn btn-danger" th:text="#{settings.signOut}">Sign Out</button>
|
||||||
</a>
|
</a>
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}"></button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}"></button>
|
||||||
|
|
Loading…
Reference in a new issue