Spring Boot logs out after custom error page - spring-boot

I have a Spring Boot application with HttpSecurity, which looks like the following.
Code
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.authorizeRequests()
.antMatchers("/*").permitAll()
.antMatchers("/static/**").permitAll()
.antMatchers("/user/**").hasAnyRole("ADMIN", "USER")
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.antMatchers("/**").denyAll()
.and()
.formLogin()
.loginPage("/login").permitAll()
.usernameParameter("email")
.defaultSuccessUrl("/user/uploads")
.and()
.logout()
.logoutSuccessUrl("/login?logout")
.permitAll();
}
I have added a custom error page (403) as displayed on the docs.spring.io website (the file structure).
Last, I created a small 403.html file:
<!DOCTYPE html>
<html lang="en" layout:decorator="layout/main">
<body>
<div layout:fragment="content">
<h1>403 - Permission Denied</h1>
<p>You do not have permission to retrieve the URL or link you requested.</p>
<p>Please contact the administrator of the referring page, if you think this was a mistake.</p>
<p>If you did this on purpose: behave and go back to the Homepage.</p>
</div>
</body>
</html>
This works: If a user is logged in and tries to access a /admin/ page, he will see the custom 403 page.
Problem
But the user is also logged out, for some reason! If I change the URL from the Access Denied page to /user/uploads (which I'm allowed to see as a logged in user), it will redirect me back to the login page.
Question
How can I make sure that the user isn't logged out when s/he sees the custom 403 page?
EDIT
Added the Spring Security debug log. It consists of 3 actions:
Log in to the website
Going to a forbidden /admin/users.html page
Clicking the 'homepage' link in the custom 403 page
Log:
2017-06-13 14:55:41.874 INFO 7144 --- [nio-8080-exec-1] Spring Security Debugger :
************************************************************
Request received for GET '/static/js/passwordChanging.js':
org.apache.catalina.connector.RequestFacade#146c683
servletPath:/static/js/passwordChanging.js
pathInfo:null
headers:
host: localhost:8080
user-agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0
accept: */*
accept-language: en-US,en;q=0.5
accept-encoding: gzip, deflate
referer: http://localhost:8080/adminconsole/
cookie: JSESSIONID=F690AA15EEAAF2DC9BD35E7CCFA5E94F
connection: keep-alive
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************
2017-06-13 14:55:41.879 INFO 7144 --- [nio-8080-exec-5] Spring Security Debugger :
************************************************************
Request received for GET '/static/js/login.js':
org.apache.catalina.connector.RequestFacade#108c693
servletPath:/static/js/login.js
pathInfo:null
headers:
host: localhost:8080
user-agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0
accept: */*
accept-language: en-US,en;q=0.5
accept-encoding: gzip, deflate
referer: http://localhost:8080/adminconsole/
cookie: JSESSIONID=F690AA15EEAAF2DC9BD35E7CCFA5E94F
connection: keep-alive
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************
2017-06-13 14:55:41.964 INFO 7144 --- [nio-8080-exec-3] Spring Security Debugger :
************************************************************
Request received for GET '/static/js/utils.js':
org.apache.catalina.connector.RequestFacade#108c693
servletPath:/static/js/utils.js
pathInfo:null
headers:
host: localhost:8080
user-agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0
accept: */*
accept-language: en-US,en;q=0.5
accept-encoding: gzip, deflate
referer: http://localhost:8080/adminconsole/
cookie: JSESSIONID=F690AA15EEAAF2DC9BD35E7CCFA5E94F
connection: keep-alive
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************

Related

Overriding default entry point in ServerHttpSecurity

I use the following ServerHttpSecurity chain:
#Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
http
// ..filters
.authorizeExchange().anyExchange()
.access(CustomHaveAnyAuthority())
// authentication
.and()
.httpBasic()
.and()
.oauth2ResourceServer().jwt()
.authenticationManager(CustomAuthenticationService())
return http.build()
}
However in newer Spring Security 5.5.0 (upgrade from 5.3.3.RELEASE) there is a fallback upon requesting endpoints without auth to BearerTokenServerAuthenticationEntryPoint instead of HttpBasicServerAuthenticationEntryPoint.
How can I override this behaviour? I tried to resort methods above, but didn't work.
Logs from application:
2021-06-02 11:50:43,206 [boundedElastic-1] DEBUG o.s.s.w.s.a.DelegatingReactiveAuthorizationManager - Checking authorization on '/endpoint' using org.springframework.security.authorization.AuthorityReactiveAuthorizationManager#73302f30
2021-06-02 11:50:43,216 [boundedElastic-1] DEBUG o.s.s.w.s.authorization.AuthorizationWebFilter - Authorization failed: Access Denied
2021-06-02 11:50:43,220 [boundedElastic-1] DEBUG o.s.s.w.s.c.WebSessionServerSecurityContextRepository - No SecurityContext found in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession#921515f'
2021-06-02 11:50:43,221 [boundedElastic-1] DEBUG o.s.s.w.s.DelegatingServerAuthenticationEntryPoint - Trying to match using OrServerWebExchangeMatcher{matchers=[org.springframework.security.config.web.server.ServerHttpSecurity$HttpBasicSpec$$Lambda$1073/0x0000000100b00040#73874030, AndServerWebExchangeMatcher{matchers=[NegatedServerWebExchangeMatcher{matcher=MediaTypeRequestMatcher [matchingMediaTypes=[text/html], useEquals=false, ignoredMediaTypes=[]]}, MediaTypeRequestMatcher [matchingMediaTypes=[application/atom+xml, application/x-www-form-urlencoded, application/json, application/octet-stream, application/xml, multipart/form-data, text/xml], useEquals=false, ignoredMediaTypes=[*/*]]]}]}
2021-06-02 11:50:43,221 [boundedElastic-1] DEBUG o.s.s.w.s.util.matcher.OrServerWebExchangeMatcher - Trying to match using org.springframework.security.config.web.server.ServerHttpSecurity$HttpBasicSpec$$Lambda$1073/0x0000000100b00040#73874030
2021-06-02 11:50:43,222 [boundedElastic-1] DEBUG o.s.s.w.s.util.matcher.OrServerWebExchangeMatcher - Trying to match using AndServerWebExchangeMatcher{matchers=[NegatedServerWebExchangeMatcher{matcher=MediaTypeRequestMatcher [matchingMediaTypes=[text/html], useEquals=false, ignoredMediaTypes=[]]}, MediaTypeRequestMatcher [matchingMediaTypes=[application/atom+xml, application/x-www-form-urlencoded, application/json, application/octet-stream, application/xml, multipart/form-data, text/xml], useEquals=false, ignoredMediaTypes=[*/*]]]}
2021-06-02 11:50:43,227 [boundedElastic-1] DEBUG o.s.s.w.s.util.matcher.AndServerWebExchangeMatcher - Trying to match using NegatedServerWebExchangeMatcher{matcher=MediaTypeRequestMatcher [matchingMediaTypes=[text/html], useEquals=false, ignoredMediaTypes=[]]}
2021-06-02 11:50:43,227 [boundedElastic-1] DEBUG o.s.s.w.s.u.m.MediaTypeServerWebExchangeMatcher - httpRequestMediaTypes=[text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8]
2021-06-02 11:50:43,227 [boundedElastic-1] DEBUG o.s.s.w.s.u.m.MediaTypeServerWebExchangeMatcher - Processing text/html
2021-06-02 11:50:43,227 [boundedElastic-1] DEBUG o.s.s.w.s.u.m.MediaTypeServerWebExchangeMatcher - text/html .isCompatibleWith text/html = true
2021-06-02 11:50:43,228 [boundedElastic-1] DEBUG o.s.s.w.s.u.m.NegatedServerWebExchangeMatcher - matches = false
2021-06-02 11:50:43,228 [boundedElastic-1] DEBUG o.s.s.w.s.util.matcher.AndServerWebExchangeMatcher - Did not match
2021-06-02 11:50:43,228 [boundedElastic-1] DEBUG o.s.s.w.s.util.matcher.OrServerWebExchangeMatcher - No matches found
2021-06-02 11:50:43,229 [boundedElastic-1] DEBUG o.s.s.w.s.DelegatingServerAuthenticationEntryPoint - Trying to match using org.springframework.security.web.server.authentication.AuthenticationConverterServerWebExchangeMatcher#21307a04
2021-06-02 11:50:43,229 [boundedElastic-1] DEBUG o.s.s.w.s.DelegatingServerAuthenticationEntryPoint - No match found. Using default entry point org.springframework.security.oauth2.server.resource.web.server.BearerTokenServerAuthenticationEntryPoint#24d2ea8a
Thanks
You can define your own way to handle the exceptions, like this:
#Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
http
.authorizeExchange().anyExchange()
.access(CustomHaveAnyAuthority())
.and()
.httpBasic()
.and()
.oauth2ResourceServer().jwt()
.authenticationManager(CustomAuthenticationService())
.exceptionHandling()
.authenticationEntryPoint(HttpBasicServerAuthenticationEntryPoint())
return http.build()
}

How to resolve 403 on spring security JWT authentication for public resources

I'm trying to implement a JWT based authentication using spring security in a spring boot API, but I don't know what I'm doing wrong. On my implementation of WebSecurityConfigurerAdapter I permit access to auth/** resource, but when I make a request to, for example, /auth/login, I get a 403. It seems that it is ignoring the "public" resources.
The csrf() is disabled.
This is the repository: https://github.com/wallysoncarvalho/jwt-auth-spring-security
I enabled DEBUG mode and that's what I get:
Request received for POST '/auth/login?username=wally&password=wally':
org.apache.catalina.connector.RequestFacade#585d8cc6
servletPath:/auth/login
pathInfo:null
headers:
user-agent: PostmanRuntime/7.26.8
accept: */*
postman-token: 91c2a071-a353-4d77-9c7c-b04a43b94081
host: localhost:8091
accept-encoding: gzip, deflate, br
connection: keep-alive
content-length: 0
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
JwtFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************
2020-12-22 20:21:15.919 DEBUG 6288 --- [nio-8091-exec-2] o.s.security.web.FilterChainProxy : Securing POST /auth/login?username=wally&password=wally
2020-12-22 20:21:15.937 DEBUG 6288 --- [nio-8091-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2020-12-22 20:21:15.948 DEBUG 6288 --- [nio-8091-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2020-12-22 20:21:15.960 INFO 6288 --- [nio-8091-exec-2] Spring Security Debugger :
************************************************************
Request received for POST '/error?username=wally&password=wally':
org.apache.catalina.core.ApplicationHttpRequest#6d88bc8c
servletPath:/error
pathInfo:null
headers:
user-agent: PostmanRuntime/7.26.8
accept: */*
postman-token: 91c2a071-a353-4d77-9c7c-b04a43b94081
host: localhost:8091
accept-encoding: gzip, deflate, br
connection: keep-alive
content-length: 0
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
JwtFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************
2020-12-22 20:21:15.961 DEBUG 6288 --- [nio-8091-exec-2] o.s.security.web.FilterChainProxy : Securing POST /error?username=wally&password=wally
2020-12-22 20:21:15.961 DEBUG 6288 --- [nio-8091-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2020-12-22 20:21:15.967 DEBUG 6288 --- [nio-8091-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2020-12-22 20:21:15.998 DEBUG 6288 --- [nio-8091-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Failed to authorize filter invocation [POST /error?username=wally&password=wally] with attributes [authenticated]
2020-12-22 20:21:16.022 DEBUG 6288 --- [nio-8091-exec-2] o.s.s.w.a.Http403ForbiddenEntryPoint : Pre-authenticated entry point called. Rejecting access
2020-12-22 20:21:16.025 DEBUG 6288 --- [nio-8091-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/assets/**",);
}
And your #Configuration class must implements WebMvcConfigurer
Edit
Also enable WebSecurity in your config class by annotating it with #EnableWebSecurity

Is it possible to define an end point in a Controller with a RequestBody and a RequestPart?

I need to create a single end point that accepts a RequestBody OR a RequestPart.
If the request contains the RequestPart it will execute some logic to process the MultipartFile otherwise it will process the object passed in the RequestBody.
I checked How to send #Requestbody and #Requestpart together in spring but it differs from my question because I don't want to send both, RequestBody and RequestPart, at the same time.
I defined my entry point as:
#RequestMapping(value="/xyz/api/{endPoint}", method= RequestMethod.POST)
public void endPointPost(
#PathVariable String endPoint,
HttpServletRequest request,
HttpServletResponse response,
#RequestBody(required=false) Object body,
#RequestPart(required=false) MultipartFile uploadFile) throws Exception {
If the request contains only the RequestBody it works correctly, for instance:
{"body":{"companyCD":"myTest"}}
However, when sending the multipart request it fails with the follwing error:
2019-10-18 00:50:43,440 DEBUG [http-nio-8080-exec-8] org.springframework.web.servlet.handler.AbstractHandlerMapping: Mapped to public void com.monoplus.mcd.rest.GenericController.endPointPost(java.lang.String,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,java.lang.Object,org.springframework.web.multipart.MultipartFile) throws java.lang.Exception
2019-10-18 00:50:43,440 INFO [http-nio-8080-exec-8] com.monoplus.mcd.rest.ServletControllerInterceptor: ServletControllerInterceptor - preHandle
2019-10-18 00:50:43,442 DEBUG [http-nio-8080-exec-8] org.springframework.web.method.support.InvocableHandlerMethod: Could not resolve parameter [3] in public void com.monoplus.mcd.rest.GenericController.endPointPost(java.lang.String,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,java.lang.Object,org.springframework.web.multipart.MultipartFile) throws java.lang.Exception: Content type 'multipart/form-data;boundary=----WebKitFormBoundaryG1Xr4xtC2rNYWuCd;charset=UTF-8' not supported
2019-10-18 00:50:43,446 DEBUG [http-nio-8080-exec-8] org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: Using #ExceptionHandler public final org.springframework.http.ResponseEntity<java.lang.Object> org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest) throws java.lang.Exception
2019-10-18 00:50:43,481 DEBUG [http-nio-8080-exec-8] org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor: No match for [text/html, application/xhtml+xml, image/webp, image/apng, application/signed-exchange;v=b3, application/xml;q=0.9, */*;q=0.8], supported: []
2019-10-18 00:50:43,482 DEBUG [http-nio-8080-exec-8] org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver: Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=----WebKitFormBoundaryG1Xr4xtC2rNYWuCd;charset=UTF-8' not supported]
2019-10-18 00:50:43,483 INFO [http-nio-8080-exec-8] com.monoplus.mcd.rest.ServletControllerInterceptor: ServletControllerInterceptor - afterCompletion - org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryResponseWrapper#6c9a1e05
2019-10-18 00:50:43,484 DEBUG [http-nio-8080-exec-8] org.springframework.web.servlet.FrameworkServlet: Completed 415 UNSUPPORTED_MEDIA_TYPE
Please note that Could not resolve parameter [3]... refers to the RequestBody parameter.
This is my multipart request:
Header
Host: localhost:88
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------262541039624932
Content-Length: 1401
Connection: keep-alive
Referer: http://localhost:88/appl/html/master/FileImport.html
Cookie: JSESSIONID=3f052417-1702-48b6-b7c2-cac5609ef525; SESSION=M2YwNTI0MTctMTcwMi00OGI2LWI3YzItY2FjNTYwOWVmNTI1
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache
Body
-----------------------------262541039624932
Content-Disposition: form-data; name="uploadFile"; filename="testFile.txt"
Content-Type: text/plain
1 - File content
-----------------------------262541039624932
Content-Disposition: form-data; name="_ns"
-----------------------------262541039624932
Content-Disposition: form-data; name="_qt"
false
-----------------------------262541039624932
Content-Disposition: form-data; name="_body"
{"USER_NAME":""}
-----------------------------262541039624932--
Any help is appreciated.
Thank you
I'm thinking about this question from a RESTful point of view and not necessarily spring. If you are 1) trying to create or edit (post or put) a resource or 2) trying to upload a file; shouldn't those be two different URI Paths?
Thanks to Chris suggestion I was able to solve my question, I defined a different entry point for the Multipart content.
#RequestMapping(value="/xyz/api/{endPoint}", method= RequestMethod.POST, consumes = {"multipart/form-data"})
public void multiPartEndPointPost(
#PathVariable String endPoint,
HttpServletRequest request,
HttpServletResponse response
) throws Exception {
this.doSomeStuff(endPoint, request, response);
}
The important part is the consumes = {"multipart/form-data"} then I can use Apache Commons FileUpload to upload the files.
The answer for [Cannot use Apache Commons FileUpload with Spring Boot multipart.resolve-lazily also helped me to solve my question.

How to disable HttpOnly flag on Set-Cookie header on login in Spring Boot 2.1.0

I am having issues disabling the httpOnly flag on the set-cookie header. This is mainly an issue on login when the JSESSIONID is being sent back in the response. Note that this is on a tomcat server deployed on AWS EBS.
Any of the configs below work fine locally but no on deployment.
I have tried the following solutions, none seem to work
application.yml config
server:
servlet:
session:
cookie:
http-only: false
Servlet Context Initializer
#Bean
open fun servletContextInitializer(): ServletContextInitializer {
return ServletContextInitializer { servletContext ->
servletContext.setSessionTrackingModes(setOf(SessionTrackingMode.COOKIE))
val sessionCookieConfig = servletContext.sessionCookieConfig
sessionCookieConfig.isHttpOnly = false
}
WebServerFactoryCustomizer
#Bean
open fun tomcatCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
return WebServerFactoryCustomizer { tomcat ->
tomcat
.addContextCustomizers(TomcatContextCustomizer { context -> context.useHttpOnly = false })
}
web.xml
<session-config>
<cookie-config>
<http-only>false</http-only>
</cookie-config>
</session-config>
Sample Request Header
Host:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer:
Authorization: Bearer null
Content-Type: application/json
Content-Length: 58
Origin:
Connection: keep-alive
TE: Trailers
Sample Response Header
HTTP/2.0 200 OK
date: Sat, 16 Mar 2019 14:11:58 GMT
set-cookie: AWSALB=qBpX9uFjtkP4H7gyJ3EXL8na0a7aARiEN/twi0cc2sPywvbysKXXaNfQbe8HaS5hcC6VRnkp09VYj0pGcXiHbWRod9OithDlQ0ZIvHSbY7B5xiJT1r8N+lcRdCcp; Expires=Sat, 23 Mar 2019 14:11:57 GMT; Path=/
server: Apache/2.4.37 (Amazon) OpenSSL/1.0.2k-fips
vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers
access-control-allow-origin:
access-control-allow-credentials: true
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
strict-transport-security: max-age=31536000 ; includeSubDomains
x-frame-options: DENY
set-cookie: JSESSIONID=70F12355ABFDD0F42292D9F6CEAA22BF; Path=/; Secure; HttpOnly
X-Firefox-Spdy: h2
I was finally able to resolve it by creating a Filter that runs as part of Spring Security. The filter executes before the SecurityContextPersistenceFilter, thus waiting until the set-cookie header is added then updates the headers (before in the chain, gets last call after doFilter() executes).
Filter Implementation
package com.zambezii.app.security.filter
import org.springframework.web.filter.GenericFilterBean
import java.io.IOException
import javax.servlet.FilterChain
import javax.servlet.ServletException
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class SessionFilter : GenericFilterBean() {
#Throws(IOException::class, ServletException::class)
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
val req = request as HttpServletRequest
val res = response as HttpServletResponse
chain.doFilter(req, res)
removeHttpOnlyFlag(res)
}
private fun removeHttpOnlyFlag(res: HttpServletResponse) {
val setCookieHeaderName = "set-cookie"
var setCookieHeader = res.getHeader(setCookieHeaderName)
if (setCookieHeader != null) {
setCookieHeader = setCookieHeader.replace("; HttpOnly", "")
res.setHeader(setCookieHeaderName, setCookieHeader)
}
}
}
Security Config
open class WebSecurityConfig() : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
...
.authenticated()
.and()
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter::class.java)
.addFilterBefore(SessionFilter(), SecurityContextPersistenceFilter::class.java)

Apache Tomcat 8.5.2 + Resteasy CORS filter stops working suddenly

I have a JAX-RS application (RestEasy, 3.1.2.Final) running on an embedded Apache Tomcat (8.5.2) instance. This is a public REST service so I have added a CORS filter from RestEasy to it:
import org.jboss.resteasy.plugins.interceptors.CorsFilter;
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new LinkedHashSet<>();
// = = = CORS = = =
CorsFilter cors = getCorsFilter();
singletons.add(cors);
//...
return singletons;
}
private CorsFilter getCorsFilter() {
CorsFilter cors = new CorsFilter();
cors.getAllowedOrigins().add("*");
cors.setAllowCredentials(true);
cors.setAllowedMethods("GET,POST,PUT,DELETE,HEAD");
cors.setCorsMaxAge(1209600);
cors.setAllowedHeaders("Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization,Accept-Encoding,Accept-Language,Access-Control-Request-Method,Cache-Control,Connection,Host,Referer,User-Agent");
return cors;
}
There is a security constraint defined in web.xml as well:
<security-constraint>
<web-resource-collection>
<web-resource-name>Tango RESTful gateway</web-resource-name>
<url-pattern>/rest/*</url-pattern>
<http-method>GET</http-method>
<http-method>HEAD</http-method>
<http-method>POST</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>desy-user</role-name>
<role-name>mtango-rest</role-name>
</auth-constraint>
</security-constraint>
In this setup everything works fine BUT for a few weeks. After a few weeks CORS preflight fails with 401 instead of normal sequence:
Request:
Host: mstatus.esrf.fr
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Origin: https://ingvord.github.io
DNT: 1
Connection: keep-alive
Response:
Connection: Keep-Alive
Content-Length: 62
Content-Type: application/octet-stream
Date: Sun, 17 Sep 2017 07:28:19 GMT
Keep-Alive: timeout=5, max=85
Server: Apache-Coyote/1.1
Obviously CORS filter is not executed anymore - there ain't response headers that it sets.
What could be the reason of a such behavior? Once again it works for a few weeks after a restart.
Application link: https://ingvord.github.io/tango-controls.demo/
Thanks in advance,
Apparently it seems the following extra configuration in web.xml has solved the issue:
<security-constraint>
<web-resource-collection>
<web-resource-name>CORS preflight</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>OPTIONS</http-method>
</web-resource-collection>
</security-constraint>
Well, at least we do not observe any misbehavior for a quite long period of time.

Resources