I have a web application with spring security configured to limit access on both URLs and methods. I want to disable it entirely by-default, and allow my customers to easily turn it on if they want to (they can only access "spring-security.xml").
I managed to turn off the URL interception, but my method security is still enabled...
Any clue?
(I don't want to let the customer change my web.xml, so unfortunately modifying the "global-method-security" setting each time is not an option...)
This is my updated spring-security.xml configuration:
<http auto-config='true' use-expressions="true">
<intercept-url pattern="/**" access="permitAll" />
<http-basic />
<anonymous />
</http>
I have overriden the DelegatingFilterProxy.doFilter method like this:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String springSecured = System.getProperty("springSecured");
if (StringUtils.isNotBlank(springSecured) && springSecured.equalsIgnoreCase("true")) {
// Call the delegate
super.doFilter(request, response, filterChain);
} else {
// Ignore the DelegatingProxyFilter delegate
filterChain.doFilter(request, response);
}
}
and this is an example of the method security I have:
#RequestMapping(
value = "applications/{applicationName}/timeout/{timeout}",
method = RequestMethod.POST)
public
#ResponseBody
#PreAuthorize("isFullyAuthenticated() and hasPermission(#authGroups, 'deploy')")
Object deployApplication() {
// ...
}
If I were you I wouldn't use a custom filter chain implementation, just the one out of the box. You can enable and disable sections of bean configuration (since Spring 3.0) with nested elements, so something like this might be convenient:
<beans profile="secure">
<http auto-config='true' use-expressions="true">...</http>
</beans>
Your application is now unprotected in the default profile (and any other but the "secure" profile). You can enable the secure profile by providing a system property spring.profiles.active=secure, or by explicitly setting it in a context or servlet initializer.
Related
Spring security offers an XML way to configure CSRF protection by applying the <csrf /> tag into the <http> element configuration.
For development purposes, I want to occasionally switch off such protection. Normally I would edit my security-context.xml file and change to <csrf disabled="true">.
I tried to use an expression such as <csrf disabled="${someProperty:false}" /> but it's not valid because XML schema accepts only a raw boolean value.
I don't want to use an entire bean profile (for the entire <http> element) just for a single parameter that has to be switched on/off sometimes.
Any advice?
Additional info
In order to perform some unit testing with RESTClient against authenticated controllers (when I am too lazy to use JUnit with MockMvc) I both need to bypass form authentication (e.g. using <http-basic /> and instructing RESTClient on the credentials) and disable CSRF, otherwise all requests will be blocked for missing token.
My application is a WAR application and by design it uses XML configuration instead of code-based configuration
Use request-matcher-ref and create your own custom RequestMatcher.
<csrf token-repository-ref="csrfTokenRepository" request-matcher-ref="csrfRequestMatcher" />
Here is for spring bean definition:
<spring:bean class="com.faizalsidek.security.CsrfRequestMatcher" id="csrfRequestMatcher" />
<spring:bean class="org.springframework.security.web.csrf.CookieCsrfTokenRepository" id="csrfTokenRepository" />
Here is the custom CsrfRequestMatcher:
public class CsrfRequestMatcher implements RequestMatcher {
/**
* Enable CSRF flag.
*/
#Value("${csrf.enabled}")
private boolean csrfEnabled;
#Override
public final boolean matches(final HttpServletRequest request) {
return csrfEnabled;
}
}
Because I stumbled upon this Question with the same problem I'd like to complement Faizal Sidek's answer, because his answer removes the default matching of Spring security.
To keep the default behaviour when csrf is enabled you need to match the requests HTTP method like this:
Your custom request matcher:
package my.example;
import org.springframework.security.web.util.matcher.RequestMatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.regex.Pattern;
public class CustomRequestMatcher implements RequestMatcher {
private Pattern allowedMethods = Pattern.compile("^(GET|TRACE|HEAD||OPTIONS)$");
private boolean enabled;
public CustomRequestMatcher(boolean enabled) {
this.enabled = enabled;
}
#Override
public boolean matches(HttpServletRequest httpServletRequest) {
return enabled && !allowedMethods.matcher(httpServletRequest.getMethod()).matches();
}
}
Then use it in your security.xml:
<http ...>
<csrf request-matcher-ref="requestMatcher"/>
</http>
<beans:bean id="requestMatcher" class="my.example.CustomRequestMatcher">
<beans:constructor-arg value="${myproperty}" />
</beans:bean>
Now if you set "myproperty" to false csrf is disabled, but if "myproperty" is true the default behaviour is preserved.
i wish to disable all kinds of session tracking features in Jetty 9 for my stateless- or manually maintained state Spring MVC application, but i failed to find any working examples showing how to do so.
I have tried the following /WEB-INF/spring-config.xml tag:
...
<security:http use-expressions="true"
disable-url-rewriting="true"
create-session="stateless">
...
Alongside with the following /WEB-INF/jetty-web.xml descriptor in war:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Get name="sessionHandler">
<Get name="sessionManager">
<Set name="usingCookies" type="boolean">false</Set>
</Get>
</Get>
</Configure>
But i am still getting JSESSIONID cookies whenever trying to open any page of my application. Any hints why and how to fix it?
With servlet 3 it is possible to set session tracking mode as a part of servlet registration - ServletContext#setSessionTrackingModes... you can try that.
However in your case I would investigate who is calling HttpServletRequest#getSession(...). Put breakpoint in this method to see who is calling it. Some piece of code in your application is initializing session.
You can accomplish the same goal by invalidating the session as soon as the request is complete. You can do that with a ServletRequestListener like this:
public class SessionKiller implements ServletRequestListener {
public void requestInitialized(ServletRequestEvent sre) {
// no-op
}
public void requestDestroyed(ServletRequestEvent sre) {
final HttpServletRequest servletRequest = (HttpServletRequest)sre.getServletRequest();
final HttpSession session = servletRequest.getSession(false);
if (session != null) {
session.invalidate();
}
}
}
To use the ServletRequestListener, add the following to the web-app element in the webapp'sweb.xml:
<listener>
<listener-class>YOUR-PACKAGE-NAME.SessionKiller</listener-class>
</listener>
An alternative to invalidating created sessions as suggested by user100464, I used a HttpSessionListener that throws Exceptions whenever someone tries to open a session, e.g. by calling request.getSession(), and removed occurences.
public class PreventSessions implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent se) {
throw new UnsupportedOperationException("sessions are not allowed");
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
throw new UnsupportedOperationException("sessions are not allowed");
}
}
Implementation of what Pavel Horal suggested in his answer, using Spring Boot, is simply this:
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;
#Configuration
public class WebContainerConfiguration {
#Bean
public ServletContextInitializer servletContextInitializer() {
return servletContext -> servletContext.setSessionTrackingModes(Collections.emptySet());
}
}
working nicely for me. Thank you!
I am trying out the HandlerInterceptors from Spring MVC 3.0.
Below is my interceptor
public class SessionInterceptor extends HandlerInterceptorAdapter {
#Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("inside preHandle");
if(request.getSession().getAttribute(SessionConsta nts.USER_SESSION_NAME) == null) {
response.sendRedirect("welcome");
return false;
}
return true;
}
}
Below is my configuration in my xml
<mvc:annotation-driven/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/services/*"/>
<bean class="com.ca.myca.interceptors.SessionInterceptor " />
</mvc:interceptor>
</mvc:interceptors>
But the interceptor is not getting called.
Please let me know if I am missing any thing.
In our application we are using double ** for any service sub-path match, so try changing it and check if it helps:
<mvc:mapping path="/services/**"/>
You are using <mvc:annotation-driven/> with mvc interceptor.
Please check on Spring reference:
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/web/servlet/DispatcherServlet.html
"When running in a Java 5+ environment, a default AnnotationMethodHandlerAdapter will be registered as well. HandlerAdapter objects can be added as beans in the application context, overriding the default HandlerAdapters. Like HandlerMappings, HandlerAdapters can be given any bean name (they are tested by type)."
<mvc:annotation-driven/> is supposed to be used for annotation-driven MVC controllers like #RequestMapping, #Controller etc, but I have seen there is no need to define "<mvc:annotation-driven/>" for supporting it.
Unless you are using jackson (for json support), you can try to remove <mvc:annotation-driven/> and use "<context:annotation-config>" instead for common use like autowiring etc.
try what is suggested in Configuration of Spring MVC and JSON using Jackson.
Put you interceptor in <mvc:interceptors> tag
<mvc:interceptors>
<bean class="xx.x..x..x...UserSessionInterceptor" />
</mvc:interceptors>
you can keep <mvc:annotation-driven/> and <context:annotation-config>
In reference to the post above by arviarya, <mvc:annotation-driven /> in the config XML results in a different handler Object being passed to the interceptor. In our interceptor method we had:
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mav) throws Exception {
if (mav != null && handler instanceof HandlerMethod) {
// something we want to have happen
}
This was being called with the #Controller-derived object without the <mvc:annotation-driven />, but was called with the HandlerMethod-derivedobject when it was present. For our if block to work, I needed the tag in our config XML.
In spring security 3.0, we are having AuthenticationProcessingFilter class, in which we were using determineTargetUrl() method, which returned the url based on different roles.
Now, we are moving to spring security 3.1.0.RC3 and I am stuck how should I now determine the url based on different roles as AuthenticationProcessingFilter class has been removed from new version. Can anyone please give me steps in brief with some code so that I can implement custom filter to redirect to different pages for different roles.
The best way to determine the target url based upon roles is to specify a target url in your Spring Security configuration as shown below. This will work in Spring 3.0 or 3.1
<http>
...
<form-login login-page="/login" default-target-url="/default"/>
</http>
Then create a controller that processes the default-target-url. The controller should redirect or forward based upon rolls. Below is an example of using Spring MVC, but any type of controller will work (i.e. Struts, a Servlet, etc).
#Controller
public class DefaultController {
#RequestMapping("/default")
public String defaultAfterLogin(HttpServletRequest request) {
if (request.isUserInRole("ROLE_ADMIN")) {
return "redirect:/users/sessions";
}
return "redirect:/messages/inbox";
}
}
The advantages to this approach are it is not coupled to any specific implementation of Security, it is not coupled to any specific MVC implementation, and it works easily with Spring Security namespace configuration. A full example can be found in the SecureMail project I presented at SpringOne this year.
An alternative is that you could create a custom AuthenticationSuccessHandler. The implementation might extend SavedRequestAwareAuthenticationSuccessHandler which is the default AuthenticationSuccessHandler. it could then be wired using the namespace as shown below.
<sec:http>
<sec:form-login authentication-success-handler-ref="authSuccessHandler"/>
</sec:http>
<bean:bean class="example.MyCustomAuthenticationSuccessHandler"/>
I would not recommend doing this as it is tied to Spring Security API's and it is better to avoid that when possible.
Using Custom Authentication Success Handler to specify the redirection based on user role after successful authentication.
You need to create Custom Authentication Success Handler as the following :
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
public class CustomeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException {
handle(request, response, authentication);
}
protected void handle(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException {
String targetUrl = determineTargetUrl(authentication);
if (response.isCommitted()) {
return;
}
redirectStrategy.sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(Authentication authentication) {
boolean isTeacher = false;
boolean isAdmin = false;
Collection<? extends GrantedAuthority> authorities
= authentication.getAuthorities();
for (GrantedAuthority grantedAuthority : authorities) {
if (grantedAuthority.getAuthority().equals("ROLE_USER")) {
isTeacher = true;
break;
} else if (grantedAuthority.getAuthority().equals("ROLE_ADMIN")) {
isAdmin = true;
break;
}
}
if (isTeacher) {
return "/user/account";
} else if (isAdmin) {
return "/admin/account";
} else {
throw new IllegalStateException();
}
}
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
protected RedirectStrategy getRedirectStrategy() {
return redirectStrategy;
}
}
Then modify spring security xml file and defined your bean and use it
<bean id="customeAuthenticationSuccessHandler"
class="com.test.CustomeAuthenticationSuccessHandler"/>
<security:http auto-config="true" use-expressions="false">
<security:form-login login-page="/sign-in" login-processing-url="/sign-in" username-parameter="username"
password-parameter="password"
authentication-success-handler-ref="customeAuthenticationSuccessHandler"
always-use-default-target="true"
authentication-failure-url="/sign-in?error=true"/>
<security:logout logout-url="/logout" logout-success-url="/"/>
..
..
</security:http>
With Spring security I configure the parts of my web app that need to be secured by https with security:intercept-url tag.
For example:
<security:intercept-url pattern="/**/*secure*" requires-channel="https"/>
<security:intercept-url pattern="/**" requires-channel="http" />
This way all requests with "secure" in the file name or directories containing "secure" on the lowest level get https, the rest will get http.
Is there a way to use a flag to make Spring force https?
I could not get anything to work that takes stuff after the "?" of an url.
http://domain/abc?secure=true => https
http://domain/abc => http
Spring would then redirect transparently every request going to http://domain/abc?secure=true to https://domain/abc?secure=true.
Is there a way to achieve that?
The first pattern in the example achieves the result for a "secure" occuring before the "?", but I am looking for a solution/pattern to have a flag after the "?" only.
To make Spring consider fragments after the "?" in urls, the stripQueryStringFromUrls has to be set to false.
Here is a solutation I found in the Spring forum:
When this BeanPostProcessor is loaded, the property will be set.
public class BeanPostProcessorImpl implements BeanPostProcessor
{
final static private Logger log = Logger.getLogger(BeanPostProcessorImpl.class);
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
{
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
{
if (bean instanceof DefaultFilterInvocationSecurityMetadataSource)
{
log.info("********* Post-processing " + beanName);
((DefaultFilterInvocationSecurityMetadataSource) bean).setStripQueryStringFromUrls(false);
}
return bean;
}
}
This way pattern
<security:intercept-url pattern="/**/*?secure=true*" requires-channel="https"/>
finally works.
As far as I can tell you want to have a URL param which to tell spring to redirect you to an https channel.
So did you try:
<security:intercept-url pattern="/**/*?secure*" requires-channel="https"/>
or if that doesn't work try an escaped '?' :
<security:intercept-url pattern="/**/*\?secure*" requires-channel="https"/>
I just had a look at the source for the FilterChainProxy (in 3.0.5.RELEASE) and it uses regular expressions to match the patterns. So you can have a look at:
org.springframework.security.web.util.RegexUrlPathMatcher#pathMatchesUrl(Object compiledPath, String url)
or
org.springframework.security.web.FilterChainProxy#getFilters(String url)
to see exactly what pattern you need.
So even if spring currently doesn't support the '?' sign you can extend your own UrlPathMatcher and inject it into the FilterChainProxy.