Following BalusC's instructions on that answer:
How to stream audio/video files such as MP3, MP4, AVI, etc using a Servlet
I added the following Context element to my Tomcat server.xml to make my media files available to Tomcat's own DefaultServlet.
<Context docBase="/home/jwi/media" path="/service/media" />
This works like charm and the media is available at:
http://localhost:8080/service/media/example.mp4
The ApplicationPath from my application (build on Jersey 2.x) is set to: #ApplicationPath("service").
Within that application I have a request filter that checks every incoming request for a valid user session.
#Provider
#PreMatching
#Priority(1)
public class SessionFilter implements ContainerRequestFilter {
#Context
private ServletContext _context;
#Context
private HttpServletRequest _request;
#Context
private HttpServletResponse _response;
public void filter(ContainerRequestContext requestContext) throws IOException {
HttpSession session = _request.getSession(false);
boolean isLoggedIn = session != null && session.getAttribute("username") != null;
boolean isLoginRequest = _request.getRequestURI().contains("login");
if (isLoggedIn || isLoginRequest) {
// Since filter chain is invoked by #Priority annotation here's nothing to do.
} else {
URI indexPage = UriBuilder.fromUri("/index.html").build();
requestContext.abortWith(Response.temporaryRedirect(indexPage).build());
}
}
}
My problem is, that filter is never called on the media elements. So when I open http://localhost:8080/service/media/example.mp4 the filter isn't called at all.
How do I add Tomcat's DefaultServlet to my request filter?
Have you considered a Servlet Filter instead?
#WebFilter("/service/media/*")
public class SessionFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
...
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
I have tried using servlet , but it is not working.
web.xml :
<display-name>Password</display-name>
<servlet>
<servlet-name>SetPassword</servlet-name>
<servlet-class>com.cx.view.SetPassword</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SetPassword</servlet-name>
<url-pattern>/view/setPassword</url-pattern>
</servlet-mapping>
I have a HTML page in [email_templates/setPassword.html]
java code :
public class SetPassword extends HttpServlet {
private static final long serialVersionUID = 7514856921920494774L;
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher requestDispatcher = request
.getRequestDispatcher("email_templates/setPassword.html");
requestDispatcher.forward(request, response);
}
//tried for both get and post request
#Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher requestDispatcher = request
.getRequestDispatcher("email_templates/setPassword.html");
requestDispatcher.forward(request, response);
}
}
Url trying to access: http://localhost:8080/view/setPassword
Spring boot serves automatically static resources from one of these directories:
/META-INF/resources/
/resources/
/static/
/public/
So try to save your page in one of these folders (I would suggest /static/ or /public/)
I'm in a Spring project, and I need of filters.
Now I have already read about "Interceptor Vs Filter" and for now I choose filter.
So I have this class as filter
public class LogFilter implements Filter {
private final Logger log = LoggerFactory.getLogger(this.getClass());
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
String ipAddress = request.getRemoteAddr();
this.log.info("IP " + ipAddress + ", Time " + new Date().toString());
chain.doFilter(req, res);
}
public void init(FilterConfig config) throws ServletException {
// Get init parameter
String testParam = config.getInitParameter("test-param");
this.log.info("Test Param:" + testParam);
}
public void destroy() {
// Add code to release any resource
}
}
And this method for registration of filter's bean in java config ( no xml configuration )
#Bean
public LogFilter filter() {
LogFilter filter = new LogFilter();
this.beanFactory.autowireBean(filter);
return filter;
}
Now this filter works for every url of my app, how can I choose which url should be "under filter" ?
EDIT
I solved this in this way
#Bean
public FilterRegistrationBean regFilter() {
FilterRegistrationBean regFilter = new FilterRegistrationBean();
regFilter.setFilter(new LogFilter());
regFilter.addUrlPatterns("/test");
return regFilter;
}
Thanks to the hint in comment, I found this
If you're using annotation to configure your web #WebFilter to annotate your Filter as per http://docs.oracle.com/javaee/6/tutorial/doc/bnagb.html.
Note that those annotation should be put in the LogFilter itself. And that it has no relation to spring bean or spring context. Those configuration are scanned and processed by the application server, e.g Tomcat, but Spring.
I have embedded Jetty server and I added servlet mapping.
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
context.addServlet(RegisterServlet.class, "/user/register");
I want to make the Dependency Injection in servlet with spring framework configuring ApplicationContext.xml. It should work the same as here:
public class RegisterServlet extends HttpServlet {
private Service service;
#Override
public void init() throws ServletException {
super.init();
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
service = context.getBean("service", Service.class);
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
...
}
but without using context.getBean("service").
This way you can have the control of servlet instantiation
Server server = new Server(port);
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(new ServletHolder(new RegisterServlet()), "/user/register");
server.setHandler(handler);
server.start();
So now you can get the servlet instance from a DI container of something
To force https in web.xml i was using this code snippet:
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
Is there an equivalent for this in Spring Java Config? I already figured out that i need a ServletSecurityElement. But how do i connect it to the rest?
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
container.addListener(new ContextLoaderListener(context));
context.register(PersistenceJPAConfig.class);
FilterRegistration filter = container.addFilter("wicket.myproject", WicketFilter.class);
filter.setInitParameter("applicationClassName", WicketApplication.class.getName());
filter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, "/*");
filter.addMappingForUrlPatterns(null, false, "/*");
HttpConstraintElement forceHttpsConstraint = new HttpConstraintElement(ServletSecurity.TransportGuarantee.CONFIDENTIAL, "");
ServletSecurityElement securityElement = new ServletSecurityElement(forceHttpsConstraint);
}
}
As John Thompson pointed out you were right there. You just needed to add the security element you defined to the servlet. On another note I noticed you had "" as the roleNames parameter to the HttpConstraintElement. This would actually cause everyone who didn't have the role "" to be denied. If you want this to work like normal (force https) don't give any roles. In the end this worked for me:
public class ApplicationInitializer implements WebApplicationInitializer {
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
private static final String DISPATCHER_SERVLET_MAPPING = "/";
#Override
public void onStartup(ServletContext container) throws ServletException {
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.register(ApplicationConfiguration.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(DISPATCHER_SERVLET_MAPPING);
// Force HTTPS, and don't specify any roles for this constraint
HttpConstraintElement forceHttpsConstraint = new HttpConstraintElement(ServletSecurity.TransportGuarantee.CONFIDENTIAL);
ServletSecurityElement securityElement = new ServletSecurityElement(forceHttpsConstraint);
// Add the security element to the servlet
dispatcher.setServletSecurity(securityElement);
}
}
I think you need to get a handle on the servlet registration, then register the security element. Try something like this:
ServletRegistration.Dynamic registration
= container.addServlet("dispatcher", new DispatcherServlet());
registration.setLoadOnStartup(1);
registration.setServletSecurity(securityElement); //your prev defined securityElement
In the case if you use Spring Security 3.2 you could do this as follows.
<security:intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="https"/>
with http to https port mappings as well.
<security:port-mappings>
<security:port-mapping http="${port.mapping.http.port}"
https="${port.mapping.https.port}" />
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
private static final String DISPATCHER_SERVLET_MAPPING = "/";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(ApplicationContext.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(DISPATCHER_SERVLET_NAME,
new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(DISPATCHER_SERVLET_MAPPING);
HttpConstraintElement forceHttpsConstraint = new HttpConstraintElement(TransportGuarantee.CONFIDENTIAL);
ServletSecurityElement securityElement = new ServletSecurityElement(forceHttpsConstraint);
dispatcher.setServletSecurity(securityElement);
}
What do you mean, connect it to the rest? Looks like you should be set. Spring will auto-detect the configuration of the Java configured WebApplicationInitializer.
Remember that WebApplicationInitializer implementations are detected
automatically -- so you are free to package them within your
application as you see fit.
See:
http://docs.spring.io/spring-framework/docs/3.1.x/javadoc-api/org/springframework/web/WebApplicationInitializer.html#onStartup(javax.servlet.ServletContext)
One way of do this is creating an HTTP filter inside your application:
#Component
#ConfigurationProperties("security.http")
public class ForceHTTPSFilter implements Filter {
public static final String X_FORWARDED_PROTO_HEADER = "x-forwarded-proto";
private boolean forceHttps = false;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if(forceHttps && !request.getProtocol().toUpperCase().contains("HTTPS") && request instanceof HttpServletRequest) {
Optional<String> protocol = Optional.ofNullable(((HttpServletRequest)request).getHeader(X_FORWARDED_PROTO_HEADER));
if(!protocol.orElse("http").equals("https")){
((HttpServletResponse)response).sendError(HttpStatus.FORBIDDEN.value(), "Please use HTTPS when submitting data to this server.");
return;
}
}
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
public boolean isForceHttps() {
return forceHttps;
}
public void setForceHttps(boolean forceHttps) {
this.forceHttps = forceHttps;
}
}
You can switch on/off the filter with a property by using #ConfigurationProperties.
Moreover, you should inspect the header x-forwarded-proto because some proxies (like Heroku) remove the protocol from the URL and store it into this header.
And, of course here's a unit test of this filter:
public class ForceHTTPSFilterTest {
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#InjectMocks
private ForceHTTPSFilter filter;
#Test
public void testAcceptHTTPRequestWhenFlagIsDisabled() throws Exception{
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getProtocol()).thenReturn("HTTP/1.1");
HttpServletResponse response = mock(HttpServletResponse.class);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
verify(chain, times(1)).doFilter(any(), any());
verify(response, never()).sendError(eq(403), anyString());
}
#Test
public void testAcceptHTTPRequestWhenFlagIsEnableAndItHasForwardedProtoHeader() throws Exception{
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getProtocol()).thenReturn("HTTP/1.1");
when(request.getHeader(ForceHTTPSFilter.X_FORWARDED_PROTO_HEADER)).thenReturn("https");
HttpServletResponse response = mock(HttpServletResponse.class);
filter.setForceHttps(true);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
verify(chain, times(1)).doFilter(any(), any());
verify(response, never()).sendError(eq(403), anyString());
}
#Test
public void testAcceptHTTPSRequest() throws Exception{
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getProtocol()).thenReturn("HTTPS/1.1");
HttpServletResponse response = mock(HttpServletResponse.class);
filter.setForceHttps(true);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
verify(chain, times(1)).doFilter(any(), any());
verify(response, never()).sendError(eq(403), anyString());
}
#Test
public void testRejectHTTPRequestWhenFlagIsEnableAndItDoesntHaveForwardedProtoHeader() throws Exception{
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getProtocol()).thenReturn("HTTP/1.1");
HttpServletResponse response = mock(HttpServletResponse.class);
filter.setForceHttps(true);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
verify(chain, never()).doFilter(any(), any());
verify(response, times(1)).sendError(eq(403), anyString());
}
}
None of the above answer are good enough when using Spring Boot and External Tomcat. Here the correct configuration:
super() must be called and the existing dispatcher servlet must be taken from the existing container.
private static final String DISPATCHER_SERVLET_NAME = "dispatcherServlet";
#Override
public void onStartup(ServletContext container) throws ServletException {
super.onStartup(container);
// Get the existing dispatcher servlet
ServletRegistration.Dynamic dispatcher = (ServletRegistration.Dynamic)
container.getServletRegistration(DISPATCHER_SERVLET_NAME);
// Force HTTPS, and don't specify any roles for this constraint
HttpConstraintElement forceHttpsConstraint =
new HttpConstraintElement(ServletSecurity.TransportGuarantee.CONFIDENTIAL);
ServletSecurityElement securityElement =
new ServletSecurityElement(forceHttpsConstraint);
// Add the security element to the servlet
dispatcher.setServletSecurity(securityElement);
}