get request header in a Filter - spring

I registered a Filter in Spring boot and been trying to get and Header from the request but getting null.
#Component
#Order(1)
public class ExampleFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws... {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
//null**
final String header = httpServletRequest.getHeader(HEADER);
}
BUT
#GetMapping(value = "example")
public ResponseEntity<Example> example( #RequestHeader(HEADER) String header) {
... the header is NOT null
}

Please check my example here
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomFilter implements Filter {
public CustomFilter() {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
HttpServletRequest httpRequest = (HttpServletRequest) request;
Enumeration<String> headerNames = httpRequest.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
System.out.println("Header: " + name + " value:" + httpRequest.getHeader(name));
}
}
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}

Related

How to add a header on an auth redirect response with Spring?

For integration of Spring Boot with htmx, I need a way to add a header if an incoming request is done by htmx and the user is no longer logged on.
In the normal flow, the user gets redirected to the login page. However, when there is a request done by htmx, this is an AJAX request and the redirect is not happening.
The recommended solution is that if there is an HX-Request header on the request, that the server should put an HX-Refresh: true header on the response. This will make htmx do a full page refresh.
My security config looks like this:
#Configuration
public class WebSecurityConfiguration {
private final ClientRegistrationRepository clientRegistrationRepository;
public WebSecurityConfiguration(ClientRegistrationRepository clientRegistrationRepository) {
this.clientRegistrationRepository = clientRegistrationRepository;
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests(registry -> {
registry.mvcMatchers("/actuator/info", "/actuator/health").permitAll();
registry.mvcMatchers("/**").hasAuthority(Roles.ADMIN);
registry.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll();
registry.anyRequest().authenticated();
});
http.oauth2Client();
http.oauth2Login();
http.logout(logout -> logout.logoutSuccessHandler(oidcLogoutSuccessHandler()));
return http.build();
}
private LogoutSuccessHandler oidcLogoutSuccessHandler() {
OidcClientInitiatedLogoutSuccessHandler logoutSuccessHandler = new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository);
// Sets the location that the End-User's User Agent will be redirected to
// after the logout has been performed at the Provider
logoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}");
return logoutSuccessHandler;
}
}
I tried with a Filter:
public Filter htmxFilter() {
return new Filter() {
#Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
filterChain.doFilter(servletRequest, servletResponse);
String htmxRequestHeader = request.getHeader("HX-Request");
System.out.println("htmxRequestHeader = " + htmxRequestHeader);
System.out.println(response.getStatus());
if (htmxRequestHeader != null
&& response.getStatus() == 302) {
System.out.println("XXXXXXXXXXX");
response.setHeader("HX-Refresh", "true");
}
}
};
}
But response.getStatus() is never 302 (altough I can see the 302 response status in Chrome).
I also tried with an Interceptor:
#Bean
public HandlerInterceptor htmxHandlerInterceptor() {
return new HandlerInterceptor() {
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
boolean htmxRequest = request.getHeader("HX-Request") != null;
String htmxRequestHeader = request.getHeader("HX-Request");
System.out.println("htmxRequestHeader = " + htmxRequestHeader);
System.out.println(response.getStatus());
if( htmxRequest && response.getStatus() == 302) {
response.setHeader("HX-Refresh", "true");
}
}
};
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeInterceptor());
registry.addInterceptor(htmxHandlerInterceptor());//.order(Ordered.HIGHEST_PRECEDENCE);
}
Which also does not work, there is no 302 response status.
I also tried with the commented out order(Ordered.HIGHEST_PRECEDENCE), but that did not make any difference.
Are there other options?
When a request comes to a protected endpoint and it is not authenticated, Spring Security executes its AuthenticationEntryPoints interface to commence an authentication scheme.
You could create your own AuthenticationEntryPoint that adds the header and delegates to the LoginUrlAuthenticationEntryPoint (or other implementation that you are using).
#Bean
SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
http
//...
.exceptionHandling(exception -> exception
.authenticationEntryPoint(new HxRefreshHeaderAuthenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")))
);
return http.build();
}
public class HxRefreshHeaderAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final AuthenticationEntryPoint delegate;
public HxRefreshHeaderAuthenticationEntryPoint(AuthenticationEntryPoint delegate) {
this.delegate = delegate;
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
// Add the header
this.delegate.commence(request, response, authException);
}
}
You need to make sure that your filter runs before any Spring Security filter. See at SecurityProperties.DEFAULT_FILTER_ORDER or HttpSecurity#addFilterBefore

#Value variable is null in customFilter implementing Filter

my custom filter is not taking value from .properties/.yml file
Note: property file is located at src/main/resources folder
#Slf4j
public class CustomFilter implements Filter {
#Value("${xyz.domainName:http://localhost:8080/x1}")
private String DOMAIN_NAME;
private static final String REDIRECT_URL_ENDPOINT = "/v1/xyz/abc/";
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String url = request.getRequestURL().toString();
String id = url.substring(url.lastIndexOf("/") + 1);
if (url.startsWith(DOMAIN_NAME)) {
ServletContext context = request.getServletContext();
RequestDispatcher dispatcher = context.getRequestDispatcher(REDIRECT_URL_ENDPOINT + id);
dispatcher.forward(request, response);
}
else
filterChain.doFilter(request, response);
}
}
Edit (Added WebSecurityConfigClass): My WebSecurityConfig class looks like:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new CustomFilter(), ApiKeyAuthFilter.class);
}
}
Looking at the code, you filter is not managed by the Spring Context, therefore Spring-related functionalities like #Value do not work. So, let Spring take care of your filter.
#Component
#Slf4j
public class CustomFilter implements Filter {
#Value("${xyz.domainName:http://localhost:8080/x1}")
private String DOMAIN_NAME;
private static final String REDIRECT_URL_ENDPOINT = "/v1/xyz/abc/";
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String url = request.getRequestURL().toString();
String id = url.substring(url.lastIndexOf("/") + 1);
if (url.startsWith(DOMAIN_NAME)) {
ServletContext context = request.getServletContext();
RequestDispatcher dispatcher = context.getRequestDispatcher(REDIRECT_URL_ENDPOINT + id);
dispatcher.forward(request, response);
}
else
filterChain.doFilter(request, response);
}
#Bean
public FilterRegistrationBean registerFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(this);
registration.addUrlPatterns("/*");
return registration;
}
}
If you want to register the filter before a Spring security filter you can do this:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Customfilter customfilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(customfilter, ApiKeyAuthFilter.class);
}
}

Set cookie in every request - SPRING

Im now developing simple spring boot web app..
Is there something in spring(filter) that check every request(headers)..
I want to check if there is cookie..
If there is cookie- nothing happend..
But if there is not it would create cookie...
Do i have to do this manually, so i put this in every function?
Or can i do something like global function, that got executed with every other request?
Thanks for help.
yes you have to use filters
you can try doing something like this
public class MyCookieFilter extends GenericFilterBean {
public static final String MY_COOKIE_NAME = "your-cookie-name";
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
checkCookie(httpServletRequest, httpServletResponse);
filterChain.doFilter(servletRequest, servletResponse);
}
private void checkCookie(HttpServletRequest request, HttpServletResponse servletResponse) {
boolean cookieExists = Arrays.stream(request.getCookies()).anyMatch(cookie -> cookie.getName().equalsIgnoreCase(MY_COOKIE_NAME));
if (!cookieExists) {
String cookieValue = "your-cookie-value";
Cookie newCookie = new Cookie(MY_COOKIE_NAME, cookieValue);
servletResponse.addCookie(newCookie);
}
}
}
then add it in your security config
#Override
public void configure(HttpSecurity http) throws Exception {
http
...
.addFilter(new MyCookieFilter())
...
}

Spring Boot register a filter after spring security filter is executed

I have defined 2 filters which should run on every request, but only after SecurityContextHolder's context is set by spring boot.
However, i always get SecurityContextHolder.getContext().getAuthentication() as null.
Here is my filter configuration:
#Bean
public FilterRegistrationBean SecurityContextHystrixRequestVariableSetterBean() throws Exception {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(securityContextHystrixRequestVariableSetterFilter());
registration.setOrder(Ordered.LOWEST_PRECEDENCE);
return registration;
}
#Bean
public FilterRegistrationBean HystrixRequestContextEnablerFilterBean() throws Exception {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(hystrixRequestContextEnablerFilter());
registration.setOrder(Ordered.LOWEST_PRECEDENCE);
return registration;
}
Filter details:
public class SecurityContextHystrixRequestVariableSetterFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
SecurityContextHystrixRequestVariable.getInstance().set(SecurityContextHolder.getContext());
chain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
public class HystrixRequestContextEnablerFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
chain.doFilter(request, response);
} finally {
context.shutdown();
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
You can use OncePerRequestFilter:
public class CustomFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
//do
chain.doFilter(request, response);
}
}
#Configuration
public class CustomConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterAfter(new SecurityFilter(authenticationManager()), AnonymousAuthenticationFilter.class)
}
}

return after call doFilter() of GenericFilterBean on Spring Boot

I am using GenericFilterBean as filter on my Spring boot project.
In some case, I want to pass next filter on my filter logic.
My filter looks like below;
public class MyFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (anyCondition){
chain.doFilter();
return;
}
if (anyCondition){
chain.doFilter();
return;
}
if (anyCondition){
chain.doFilter();
return;
}
chain.doFilter();
}
}
it calls other chain.doFilter() if I don’t return, that’s way I return.
İt will caused any problem to return after call chain.doFilter() ?
Is it right way to handle this situations?
Your code will always call chain.doFilter (not doChain)
for example
public class MyFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
//this gets executed before other filters
some code here
//now we execute other filters
chain.doFilter(request, response);
//this code gets executed after the 'next' filters are done
some code here
}
}
But your code always executes the next filter and then is complete.
Your code is fine, but you can do it with an if then else statement instead
public class MyFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (condition1){
//do something for condition1
} else if (condition2) {
//do something for condition2
} else if (condition3) {
//do something for condition3
}
chain.doChain();
}
}

Resources