Is this bad practice in Spring - spring

#Controller
public class View implements InitializingBean {
#GetMapping("log")
public String log() {
return "log";
}
#Override
public void afterPropertiesSet() throws Exception {
System.out.println("init");
}
}
server.xml
<Host name="localhost" appBase="webapps"
autoDeploy="false" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">>
<Context crossContext="true" debug="5" docBase="/data/project/chenshun-tag-test/code" path="" reloadable="false">
</Context>
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
When I deploy in tomcat 8.5.45 and afterPropertiesSet fails to execute, tomcat will start successfully, but the context start successfully. I can curl jsp and return 200. What I want is that tomcat fails to start, Is there a problem with my use or configuration?
ths in advance :)
➜ bin curl -I 127.0.0.1:18080/fix.jsp
HTTP/1.1 200
Set-Cookie: JSESSIONID=5223231CAB44739BEAEB0BDEA879649F; Path=/; HttpOnly
Content-Type: text/html;charset=ISO-8859-1
Transfer-Encoding: chunked
Date: Fri, 27 Dec 2019 14:08:59 GMT
➜ bin curl -I 127.0.0.1:18080/view/1.json
HTTP/1.1 500
Content-Type: text/html;charset=utf-8
Content-Language: zh-CN
Transfer-Encoding: chunked
Date: Fri, 27 Dec 2019 14:09:08 GMT
Connection: close

Currently when a servlet with load-on-startup >=0 fails its startup, the context startup is still considered as OK.
With some webapps (like spring-based ones with a DispatcherServlet), this makes no sense at all : if the servlet failed its startup, the webapp is unuseable and it would be more sensible to have tomcat mark the context as failed.
refer https://bz.apache.org/bugzilla/show_bug.cgi?id=56461

Related

Spring Boot WS-Server - Custom Http Status

I published endpoints using Spring Boot WS-Server
When I use SoapUI I see:
HTTP/1.1 200
Accept: text/xml, text/html, image/gif, image/jpeg, *; q=.2, /; q=.2
SOAPAction: ""
Content-Type: text/xml;charset=utf-8
Content-Length: 828
Date: Thu, 29 Apr 2021 14:04:54 GMT
Keep-Alive: timeout=60
Connection: keep-alive
I would like to set custom HTTP Status in response (I know that it may be against the standard but it is an external requirement). I also read following topic:
Spring WS (DefaultWsdl11Definition) HTTP status code with void
But this solution failed
Spring Boot version: 2.2.7
Problem was solved
As I said I wanted to set custom HTTP status in SOAP response.
I found this post:
Spring WS (DefaultWsdl11Definition) HTTP status code with void
Author used EndpointInterceptor with TransportContext to get HttpServletResponse, then he changed status. The difference between my and his case is the fact, that he returned void from WebService method whereas I wanted to return some response.
In my situation following code in Spring WebServiceMessageReceiverObjectSupport class (method handleConnection) overrode servlet status previously set in interceptor:
if (response instanceof FaultAwareWebServiceMessage && connection instanceof FaultAwareWebServiceConnection) {
FaultAwareWebServiceMessage faultResponse = (FaultAwareWebServiceMessage)response;
FaultAwareWebServiceConnection faultConnection = (FaultAwareWebServiceConnection)connection;
faultConnection.setFaultCode(faultResponse.getFaultCode());
}
In order to bypass this fragment of code I needed to define class with my own implementation of handleConnection method, which extended class WebServiceMessageReceiverHandlerAdapter
In my implementation I excluded change of status. Important thing is to pass WebMessageFactory bean in autowired constructor of this class, otherwise exception is raised during app's startup.
This class has to be marked with Spring stereotype (eg. #Component) and name of this bean has to be configured in Configuration class when configuring ServletRegistrationBean:
#Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext){
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
servlet.setMessageFactoryBeanName("webServiceMessageFactory");
servlet.setMessageReceiverHandlerAdapterBeanName("myOwnMessageReceiverHandlerAdapter");
return new ServletRegistrationBean<>(servlet,"/ws/*");
}

Spring Boot actuator healthcheck not found

Spring Boot 2.3.8 here. My application.yml:
server:
port: 9200
error:
whitelabel:
enabled: false
ssl:
enabled: false
spring:
cache:
type: none
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://${db.hostAndPort}/${db.name}?useSSL=false&nullNamePatternMatchesAll=true&serverTimezone=UTC
username: ${db.username}
password: ${db.password}
testWhileIdle: true
validationQuery: SELECT 1
jpa:
show-sql: false
database-platform: org.hibernate.spatial.dialect.mysql.MySQL5InnoDBSpatialDialect
hibernate:
ddl-auto: none
naming:
physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
properties:
hibernate.dialect: org.hibernate.dialect.MySQL5Dialect
hibernate.cache.use_second_level_cache: false
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: false
hibernate.hbm2ddl.auto: validate
management:
port: 9200
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: prometheus
I then wrote my own custom HealthIndicator impl:
#Component
#Slf4j
public class MyAppHealthIndicator implements HealthIndicator {
#Override
public Health health() {
Health.Builder health = Health.up();
return health.build();
}
}
When I run my app, it starts up just fine. But when I go to run a curl against the health check endpoint:
$ curl -k -i -H "Content-Type: application/json" -X GET http://localhost:9200/actuator/health
HTTP/1.1 404
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
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
X-Frame-Options: DENY
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 31 Jan 2021 01:30:39 GMT
{"timestamp":"2021-01-31T01:30:39.206+00:00","status":404,"error":"Not Found","message":"","path":"/actuator/health"}
Have I forgotten a step? Do I need to add anything? Anything wrong with my YAML file? Why can't it find my health check url?
You don't need a custom MyAppHealthIndicator class as Spring Boot comes with a default health indicator which is actived by default via the Endoint /actuator/health/
You're problem is the application.yml configuration where you define that you only want to expose the prometheus endpoint. But you should enable also the health endpoint:
...
endpoints:
web:
exposure:
include: health, prometheus
...
Now you can access it via: http://localhost:9200/actuator/health/
For more details see the offical docs: https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-endpoints

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.

How can I logon and POST to RESTful resource protected by Spring Security?

I am creating a RESTful web appliaction and am using Spring for the backend. When I wasn't implementing Spring Security, I could successfully add a new record to the JPA entity "Timestamp" by using a curl command from the command line. I am using Spring Security now so that when a user is not authenticated they are redirected to the login page. However now when I try curl to add a record with the following:
curl -i -X POST -v -u myusername:mypass -H "Content-Type:application/json" -d '{ "timestamp" : "2016-06-16T08:17:20.000Z", "peopleIn" : "1", "peopleOut":"0", "venue": "localhost://8181/venues/12" }' http://localhost:8181/timestamps
This is displayed in the terminal:
* Trying ::1...
* Connected to localhost (::1) port 8181 (#0)
* Server auth using Basic with user 'harry.quigley2#mail.dcu.ie'
> POST /timestamps HTTP/1.1
> Host: localhost:8181
> Authorization: Basic aGFycnkucXVpZ2xleTJAbWFpbC5kY3UuaWU6b2s=
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Type:application/json
> Content-Length: 118
>
* upload completely sent off: 118 out of 118 bytes
< HTTP/1.1 302 Found
HTTP/1.1 302 Found
< Server: Apache-Coyote/1.1
Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
Pragma: no-cache
< Expires: 0
Expires: 0
< X-Frame-Options: DENY
X-Frame-Options: DENY
< Set-Cookie: JSESSIONID=A8EDFA6339DA76B11E0CDF6BB566A748; Path=/; HttpOnly
Set-Cookie: JSESSIONID=A8EDFA6339DA76B11E0CDF6BB566A748; Path=/; HttpOnly
< Location: http://localhost:8181/login
Location: http://localhost:8181/login
< Content-Length: 0
Content-Length: 0
< Date: Sat, 28 May 2016 16:35:53 GMT
Date: Sat, 28 May 2016 16:35:53 GMT
I am getting a 302 redirect. Also every line is repeated twice - I'm not sure why this is.
My Spring Security Config is:
#Configuration
#EnableWebSecurity
#ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(UserDetailsService userDetailsService, AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/venue/new", "/static/**").permitAll()
.anyRequest().authenticated();
http.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
http.csrf().disable();
}
}
If someone could understand why I'm being redirected and advise on what to change in order to be able to POST to the JPA entity, that would be great! Thanks
To authenticate with cURL you need to setup HTTP Basic authentication:
http.httpBasic();

Resources