How to add headers to requests ignored by Spring Security - spring-mvc

My configuration of Spring Security is
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**"); // #3
}
Taken from here.
The documentation for ignorig says
Allows adding RequestMatcher instances that should that Spring Security should ignore. ... Typically the requests that are registered should be that of only static resources.
I would like to add some headers to files served from resources.
E.g.: Strict-Transport-Security: max-age=31536000, X-Content-Type-Options: nosniff.
How I can do it?

One solution it to change it to
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/resources/**").permitAll()
.and()
.antMatcher("/resources/**").headers().cacheControl()
}
Example how to allow cache control headers PLUS ALL DEFAULT SPRING SECURITY HEADERS.

I have struggled with the same problem. When I ignore specific requests in WebSecurity, the headers were gone.
I fixed the missing headers, by applying a filter on each request that adds my headers.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(securityHeaderFilter, BasicAuthenticationFilter.class)
...
}
The filter code looks like this. The important thing to note here, is that the Filter must be declared as a #Component. When you miss the #Component annotation, the filter will be ignored.
#Component
public class SecurityHeaderFilter implements Filter {
#Override
public void init(FilterConfig fc) throws ServletException {
// Do nothing
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader(
"custom-header1", "header-value1");
httpServletResponse.setHeader(
"custom-header2", "header-value2");
chain.doFilter(request, response);
}
#Override
public void destroy() {
// Do nothing
}
}

I have used the following solution:
#Bean
public FilterRegistrationBean setHeaders() {
HstsHeaderWriter hstsHeaderWriter = new HstsHeaderWriter(31536000, true);
XContentTypeOptionsHeaderWriter xContentTypeOptionsHeaderWriter = new XContentTypeOptionsHeaderWriter();
List<HeaderWriter> headerWriters = new ArrayList<>();
headerWriters.add(hstsHeaderWriter);
headerWriters.add(xContentTypeOptionsHeaderWriter);
HeaderWriterFilter headerWriterFilter = new HeaderWriterFilter(headerWriters);
FilterRegistrationBean bean = new FilterRegistrationBean(headerWriterFilter);
bean.setOrder(1);
return bean;
}
The above bean will add a filter globally on all the resources(even the ignoring ones). You can checkout the various implementations of org.springframework.security.web.header.HeaderWriter.java for the different kinds of security headers and add them all to HeaderWriterFilter.java.

Related

Gated Content for assets in aem

I need to create a gated content setup in aem. So basically, the page ( which contains some assets) should only be reachable after the user has submitted a form. And the assets should also be not accessible directly without submitting the form. So I have created a servlet filter to achieve this, the page logic works fine but when I access assets directly the request is not reaching the servlet filters. The below is my code please let me know if there is some mistake or there is any other preferred method, I have also tried setting this ServiceRanking(1)
#Component(service = Filter.class,
property = {
EngineConstants.SLING_FILTER_SCOPE + "=" + EngineConstants.FILTER_SCOPE_REQUEST,
})
#ServiceRankin(-700)
public class GatedContentFilter implements Filter {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response,
final FilterChain filterChain) throws IOException, ServletException {
final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
final SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) response;
final Resource resource = slingRequest.getResource();
if(resource.getPath().startsWith("/content/abc")) {
//page logic
}
else if (resource.getPath().startsWith("/content/dam/abc/gated-assets")) {
//assets logic
}
filterChain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}

Adding a custom filter to be invoked after spring-security filter in a Servlet 3+ environment

I'm using Spring-Security 3.2.4 and Spring Boot 1.1.0 (and it's related dependencies versions 4.X).
I'm writing a web application that will be run in an embedded tomcat.
I'm trying to add two additional filters(not related to Spring security) that one of them will be invoked before the Spring-Security-FilterChainProxy and the other one will be invoked after the Spring-Security-FilterChainProxy.
My Spring-Security configuration files:
#Configuration
#EnableWebMvcSecurity
public class SecurityCtxConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("pass").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.usernameParameter("user").passwordParameter("password");
}
}
And the Main class (Application.class):
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
#Bean
RequestFilter beforeSpringSecurityFilter(){
return new RequestFilter();
}
#Bean
RequestFilter afterSpringSecurityFilter(){
return new RequestFilter();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And the Filter implementation:
public class RequestFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
}
}
Is there a way to controll the invocation order when taking in account the FilterChainProxy (that is beeing created by the WebSecurityConfigurerAdapter ?
To be percise, the required order is:
request-filter-1
Spring-Security FilterChain
request-filter-2
Thanks
Agree with everything stated by Dave Syer ;) but wished to add a Java Config example of using the FilterRegistrationBean.
In my situation, I was finding that my custom security filter (using Spring Security) was being fired twice for every request. Adding the FilterRegistrationBean config fixed this.
#Bean(name = "myFilter")
public MyAuthenticationFilter myAuthenticationFilter(final MyAuthenticationEntryPoint entryPoint) {
final MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setEntryPoint(entryPoint);
return filter;
}
/**
* We do this to ensure our Filter is only loaded once into Application Context
*
*/
#Bean(name = "authenticationFilterRegistration")
public FilterRegistrationBean myAuthenticationFilterRegistration(final MyAuthenticationFilter filter) {
final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(filter);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
(Regarding my specific issue of filter being registered twice in Application Context - Rather than using a FilterRegistrationBean, I also found re-implementing the MyAuthenticationFilter to inherit from OncePerRequestFilter instead of GenericFilterBean also worked. However, OncePerRequestFilter support is from Servlet 3.x upwards and since I was writing a public library, support from Servlet 2.x may be needed)
The FilterChainProxy use by Spring Security is not Ordered (if it was you could order all your filters). But you should be able to register it in a FilterRegistrationBean which is Ordered and register your other filters the same way. In the case of the security filter you can inject it by name into the registration bean. The others you can probably inject by calling a #Bean method.
At some point spring boot exposed the security filter as a property. This is now pretty easy to do.
In you application.yml:
spring:
security:
filter:
order: 20
And some filter you want to invoke after Spring Security does it's thing:
#Bean
public FilterRegistrationBean<Filter> afterAuthFilterRegistrationBean() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
//a filter that extends OncePerRequestFilter
AfterAuthFilter afterAuthFilter = new AfterAuthFilter();
registrationBean.setFilter(afterAuthFilter);
//this needs to be a number greater than than spring.security.filter.order
registrationBean.setOrder(30);
return registrationBean;
}
For a filter that is executed before Spring security, set the order to a number less than 20.
If you are using web.xml approaches, you can follow this:
https://stackoverflow.com/a/11929129/1542363
If you using Java config approaches, you can do this in WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(your-request-filter-1, ChannelProcessingFilter.class);
http.addFilterAfter(your-request-filter-2, SwitchUserFilter.class);
}
Always check the library version you are using, and refer to the specific document for the correct order of the filter chains:
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#ns-custom-filters
Or, if you using AbstractSecurityWebApplicationInitializer, you can use the insertFilters or appendFilters.
public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
More info You can refer this:
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-multipart

how to disable web page cache throughout the servlets

To no-cache web page, in the java controller servlet, I did somthing like this in a method:
public ModelAndView home(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView(ViewConstants.MV_MAIN_HOME);
mav.addObject("testing", "Test this string");
mav.addObject(request);
response.setHeader("Cache-Control", "no-cache, no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
return mav;
}
But this only works for a particular response object. I have many similar methods in a servlet. And I have many servlets too.
If I want to disable cache throughout the application, what should I do?
(I do not want to add above code for every single response object).
Why not do this via a filter?
A filter is an object that can transform the header and content (or both) of a request or response. 
...
The main tasks that a filter can perform are as follows:
...
Modify the response headers and data. You do this by providing a customized version of the response.
Just register your Filter (class implementing the Filter interface) and modify your response within the doFilter method.
EDIT: E.g.
#WebFilter("/*")
public class NoCacheFilter implements javax.servlet.Filter {
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Cache-Control", "no-cache, no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
filterChain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
Note that the #WebFilter annotation will require Servlet 3.0, otherwise you can register it via your web.xml. This path of "/*", would apply to any path of your application, but could be narrowed in scope.

How to prevent spam request in Servlet

I'm writing a small servlet to prevent spam requests from an J2ME app. But, i don't know how to do this.
Could you help me or suggest to me some links/posts about this?
I assume you have another Servlet that handles 'valid' requests and you want spam requests to be filtered out?
If that is so, then you need a Filter.
You would configure it in your web.xml (or by annotation) to be applied to all requests going to your actual Servlet and implement it like that:
public class SpamFilter implements Filter {
#Override
public void init(FilterConfig config) throws ServletException {
// maybe read some configuration, e.g. rules that say what is spam and what is not
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (isValidRequest(request)) {
chain.doFilter(request, response);
} else {
// request is spam, prevent further processing (so, do nothing)
}
}
#Override
public void destroy() {}
}

HTTP Status 405 - HTTP method is not supported by this URL

I have the following servlet:
public class MyServlet extends HttpServlet {
private static final long serialVersionUID = 16252534;
private static int ping = 3000;
private Thread t;
private static boolean shouldStop = false;
#Override
public void init() throws ServletException {
super.init();
t = new Thread(new Runnable() {
#Override
public void run() {
while(!shouldStop) {
System.out.println("Now:" + System.currentTimeMillis());
try {
Thread.sleep(ping);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
super.doGet(req, resp);
System.out.println("doGet");
PrintWriter out = resp.getWriter();
out.println("<html><h1>It works!!</h1></html>");
}
#Override
public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
System.out.println("service");
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
System.out.println("doPost");
}
#Override
public void destroy() {
super.destroy();
System.out.println("Destroy servlet");
shouldStop = true;
}
}
Which is mapped as follows in my web.xml:
<display-name>MyServer</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.myserver.MyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
When I open my browser (Chrome) on http://localhost:8080/MyServer/MyServlet, then I see "service" from doService() being logged on console and my thread works correctly, however I don't see "It Works" from doGet() being logged and I get the following error in the browser:
HTTP method GET is not supported by this URL
How is this caused and how can I solve it?
This is the default response of the default implementation of HttpServlet#doXxx() method (doGet(), doPost(), doHead(), doPut(), etc). This means that when the doXxx() method is not properly being #Overriden in your servlet class, or when it is explicitly being called via super, then you will face a HTTP 405 "Method not allowed" error.
So, you need to make sure that you have the doXxx() method properly declared conform the API, including the #Override annotation just to ensure that you didn't make any typos. E.g.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
}
And you also need to make sure that you don't ever call super.doXxx() in your servlet method:
super.doGet(request, response);
Your servlet has this. Just get rid of this line and your problem shall disappear.
The HttpServlet basically follows the template method pattern where all non-overridden HTTP methods returns this HTTP 405 error "Method not supported". When you override such a method, you should not call super method, because you would otherwise still get the HTTP 405 error. The same story goes on for your doPost() method.
This also applies on service() by the way, but that does technically not harm in this construct since you need it to let the default implementation execute the proper methods. Actually, the whole service() method is unnecessary for you, you can just remove the entire method from your servlet.
The super.init(); is also unnecessary. It's is only necessary when you override the init(ServletConfig), because otherwise the ServletConfig wouldn't be set. This is also explicitly mentioned in the javadoc. It's the only method which requires a super call.
Unrelated to the concrete problem, spawning a thread in a servlet like that is a bad idea. For the correct approach, head to How to run a background task in a servlet based web application?
you have overridden the service method which is responsible to delegate the call to doGet or doPost. see this for more details
Also get rid of super.doxxx(..) calls from each method.
Don't override the service method and you should see, "It Works" from doGet.

Resources