SAML OAuth Integration - toggle SAML - spring-security

We are implementing the flexibility to configure OAuth or SAML or both OAuth and SAML. Configured the following in the saml security context:
<security:http pattern="/oauth/authorize/**" entry-point-ref="samlEntryPoint" use-expressions="true">
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter" />
........
........
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint" />
<security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter" />
<security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter" />
<security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter" />
<security:filter-chain pattern="/oauth/authorize/**" filters="samlEntryPoint" />
</security:filter-chain-map>
</bean>
There is a configurable property which determines whether SAML is enabled or disabled. How can I skip the samlEntryPoint from getting invoked when SAML is disabled? Application is always restarted when toggling SAML, I don't have to consider the use case of switching it on/off when the application is running.
Any help is appreciated.

How can I skip the samlEntryPoint from getting invoked when SAML is disabled?
To have various authentication schemes, you can use Spring profiles and write separate security contexts files. This is how you do it :
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Spring Security configuration for SAML only authentication -->
<beans profile="auth-saml">
<import resource="security/applicationContext-security-saml.xml" />
</beans>
<!-- Spring Security configuration for OAUTH only authentication -->
<beans profile="auth-oauth">
<import resource="security/applicationContext-security-oauth.xml" />
</beans>
<!-- Spring Security configuration for SAML+OAUTH authentication -->
<beans profile="auth-saml-oauth">
<import resource="security/applicationContext-security-saml-oauth.xml" />
</beans>
</beans>
Then you choose the active Spring profile with the environment variable spring.profiles.active with value corresponding to the profile attribute value (either auth-saml, auth-oauth or auth-saml-oauth).

In addition to Gregoire's response, you can also create a class such as multiAuthenticationEntryPoint -which takes those entrypoints as property- where you can implement
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
// TODO Auto-generated method stub
if(sth)
{
customAuthenticationEntryPoint.commence(request, response, authException);
return;
}
else {
samlEntryPoint.commence(request, response, authException);
return;
}
}

Related

SAML Spring Security session timeout

I have configured SAML SSO which is working fine. When session is expired it gives following in log.
2017-04-15 15:14:16,933 [http-nio-7070-exec-8] INFO org.springframework.boot.actuate.audit.listener.AuditListener - AuditEvent [timestamp=Sat Apr 15 15:14:16 IST 2017, principal=anonymousUser, type=AUTHORIZATION_FAILURE, data={message=Access is denied, type=org.springframework.security.access.AccessDeniedException}]
2017-04-15 15:14:17,035 [http-nio-7070-exec-8] INFO org.springframework.security.saml.log.SAMLDefaultLogger - AuthNRequest;SUCCESS;0:0:0:0:0:0:0:1;com.hbo.sso:portal;http://www.okta.com/xxxxxxx;;;
Here is my spring-security.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<import resource="classpath*:applicationContext-saml.xml" />
<security:http entry-point-ref="samlEntryPoint" use-expressions="true">
<security:intercept-url .... />
/* Logging out user */
<security:intercept-url pattern="/${myapp.logout.url}" access="permitAll()"/>
</security:http>
</beans>
When the session is expired your spring session is cleared automatically, so it throws Access Denied Exception, Solution could be 1. in your spring-security.xml add access denied page redirection details..
e.g.
<security:http >
<security:access-denied-handler error-page="/anonymous/accessdeniedpage.jsp"/>
</security:http>
or
2. If you IDP allows the configuration/redirection page on session time out, then map that to your login page.

Spring Security UserDetailsService not called

I'm upgrading from Spring Security 3.2.5 to 4.0.4, working with the migration guide.
My UserDetailsService looks like this:
package com.me.security;
import org.springframework.security.core.userdetails.UserDetailsService;
public class Users implements UserDetailsService {
public Users() {
System.err.println("USERS CONSTRUCTOR");
}
#Override
public UserDetail loadUserByUsername(String name) {
System.err.println("LOAD BY USER NAME " + name);
throw new UsernameNotFoundException("User not found.");
}
}
My WEB-INF/applicationContext.xml has this:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd">
<security:http disable-url-rewriting="true" use-expressions="false">
<security:intercept-url pattern="/auth/**" access="ROLE_ANONYMOUS"/>
<security:intercept-url pattern="/dashboard/**" access="ROLE_ADMIN,ROLE_USER"/>
<!-- ...more intercept-urls... -->
<security:access-denied-handler error-page="/pages/general/403.xhtml"/>
<security:form-login login-page="/auth/login.html"
username-parameter="j_username"
password-parameter="j_password"
login-processing-url="/j_spring_security_check"
default-target-url="/dashboard/"
authentication-failure-url="/auth/error.html"/>
<security:logout logout-success-url="/auth/login.html"
logout-url="/auth/login.html"
delete-cookies="JSESSIONID"
invalidate-session="true" />
<security:session-management invalid-session-url="/auth/login.html"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref='userDetailsService'/>
</security:authentication-manager>
<bean id="userDetailsService" class="com.me.security.Users"/>
</beans>
When I try to log in, my code does not get called. I do see the message from the Users constructor in the server logs, but not the one from its loadUserByUsername method.
Instead, no matter what I enter for user name and password, I get to my 403 error page.
(Maybe I've been looking at this for too long already...)
Why doesn't Spring call my UserDetailsService and what do I need to get it to work?
It sounds to be the csrf filter. In Spring Security 4.x it is activated by default: Migrating from Spring Security 3.x to 4.x. This may be problem if you are allways getting an HTTP 403.
Try disabling setting this inside the security:http element:
<csrf disabled="true"/>

How to keep SecurityContext set through WithSecurityContextFactory in Spring Security tests?

I'm using Spring 4.1.5 and Spring Security 4.0.0.RELEASE.
I read http://spring.io/blog/2014/05/07/preview-spring-security-test-method-security (nice article by Rob Winch) and developed my own implementation of WithSecurityContextFactory to be able to test my Spring MVC controllers:
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {
#Override
public SecurityContext createSecurityContext(WithMockCustomUser customUser) {
final User fakeUser = new User();
final SecurityUser principal = new SecurityUser(fakeUser);
final Authentication auth = new UsernamePasswordAuthenticationToken(principal, "password", HelpersTest.getAuthorities(customUser.faps()));
final SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(auth);
return context;
}
}
My abstract resource test class is as follow:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations =
{
"classpath:spring/mock-daos-and-scan-for-services.xml",
"classpath:security.xml",
"classpath:singletons.xml",
"classpath:controller-scan.xml",
"classpath:servlet.xml" })
#TestExecutionListeners(listeners=
{
ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
WithSecurityContextTestExcecutionListener.class })
public abstract class AbstractResourceMockMvcTest {
#Autowired
private WebApplicationContext wac;
#Autowired
private Filter springSecurityFilterChain;
private MockMvc mockMvc;
[...]
#Before
public void setup() {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(this.getWac())
.addFilters(springSecurityFilterChain)
.build();
}
[...]
}
Then, my concrete test class inherits from AbstractResourceTest (from above) and it uses the following annotation on a #Test-enabled method:
#WithMockCustomUser(faps={"promotion_read"})
Tracing the code, I can confirm WithMockCustomUserSecurityContextFactory.createSecurityContext() is called and its return value is set in SecurityContextHolder.setContext() (through TestSecurityContextHolder.setContext()).
So far, so good !
However, later in the process, SecurityContextPersistenceFilter.doFilter() calls SecurityContextHolder.setContext() and this overwrites the context set by the test and I lose track of the mocked security context I prepared.
security.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
"
>
<!-- HTTP security handling -->
<security:http use-expressions="true">
<security:logout logout-url="/j_spring_security_logout" invalidate-session="true" logout-success-url="/login.jsp?loggedout=true" />
<security:custom-filter before="FIRST" ref="multiTenantRequestFilter" />
<!-- make sure following page are not secured -->
<security:intercept-url pattern="/*/*/internal/**" access="hasIpAddress('127.0.0.1')" />
<!-- make sure everything else going through the security filter is secured -->
<security:intercept-url pattern="/resources/**" access="hasRole('ROLE_USER')" requires-channel="any" />
<!-- supporting basic authentication for unattended connections (web services) -->
<security:http-basic />
</security:http>
<!-- authentication strategy -->
<security:authentication-manager alias="authManager">
<security:authentication-provider user-service-ref="userSecurityService">
<security:password-encoder ref="passwordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
<!-- custom filter to intercept the tenant name from the login form -->
<bean id="multiTenantRequestFilter" class="com.meicpg.ti.web.MultiTenantRequestFilter" />
</beans>
servlet.xml:
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
"
>
<mvc:annotation-driven>
<!-- Content skipped for StackOverflow question -->
</mvc:annotation-driven>
<context:annotation-config />
<bean id="annotationExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver"></bean>
<security:global-method-security pre-post-annotations="enabled"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
How can I prevent this security context overwrite ? Does my security.xml contain an obvious flaw I missed ?
PS: I skipped the other context configuration files as they seem irrelevant to the problem.
Thanks in advance !
Unfortunately that blog post is just for method level security and does not have complete instructions for MockMvc setup (the following blog in the series does). Additionally, the blogs are actually dated (I have updated them to reflect that readers should refer to the reference documentation). You can find updated instructions in the Testing Section of the reference.
In short, update your code to the following:
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations =
{
"classpath:spring/mock-daos-and-scan-for-services.xml",
"classpath:security.xml",
"classpath:singletons.xml",
"classpath:controller-scan.xml",
"classpath:servlet.xml" })
public abstract class AbstractResourceMockMvcTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
[...]
#Before
public void setup() {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(this.getWac())
.apply(springSecurity())
.build();
}
#Test
#WithMockCustomUser(faps={"promotion_read"})
public void myTest() {
...
}
[...]
}
A few highlights:
You no longer need to provide the TestExecutionListeners
Use .apply(springSecurity()) instead of adding the spring security filter chain manually
This works because Spring Security's test support i.e. apply(springSecurity()) will override the SecurityContextRepository used by the springSecurityFilterChain to first try the TestSecurityContextHolder.

Why is my custom authentication not working in Spring Security 3?

I'm having an issue with spring security 3 while trying to implement my own Customauthentication. Following this page steps I wrote this class:
public class CustomAuth implements AuthenticationManager {
#Override
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
UserService service = new UserService();
User user = service.login((String) auth.getPrincipal(), new String(
DigestUtils.sha256((String) auth.getCredentials())));
LinkedList<GrantedAuthority> authorities = new LinkedList<>();
if (user != null) {
authorities.add(new SimpleGrantedAuthority(user.getRole()));
return new UsernamePasswordAuthenticationToken(user.getUsername(),
user.getPassword(), authorities);
}
return null;
}
}
And this is my spring-security.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:http pattern="/resources/**" security="none" />
<security:http auto-config="true" >
<security:intercept-url pattern="/user/**"
access="ROLE_USER" />
<security:intercept-url pattern="/admin/**"
access="ROLE_ADMIN,ROLE_USER" />
<security:form-login login-page="/login"
authentication-failure-url="/login?error=true" />
<security:logout invalidate-session="true" />
<security:session-management>
<security:concurrency-control
max-sessions="1" />
</security:session-management>
</security:http>
<security:authentication-manager>
<security:authentication-provider ref="myAuthProvider" />
</security:authentication-manager>
<bean id="myAuthProvider" class="org.jhonnytunes.security.CustomAuth">
</bean>
</beans>
And tomcat7 is logging this while app not displaying at browser.
Im using:
Eclipse Kepler
Ubuntu 13.04
JDK 1.7
Tomcat7
Eclipse STS plugin
What can be this?
CustomAuth should implement AuthenticationProvider, not AuthenticationManager.
implements'AuthenticationProvider' instead of 'AuthenticationManager'
'throw new BadCredentialsException (String)' instead of 'return null'

Spring Security Logout Back Button

Does spring security have a way to prevent the last point below? I'm using 3.0.5
-user logs into my website
-user goes to any page in website and clicks log out
-log out link invalidates user session and sends them to the login page in my website
-in same browser, user navigates to new website (say cnn.com)
-user hits back button and they land at my login page
-user hits back button again and they end up at the page within the application that may have data that we dont want to be there. If they click any link on the page they immediately get sent to login page, but they can view the cached page from the browser cache...any way to not let them view this?
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="dc" />
<global-method-security />
<http access-denied-page="/auth/denied.html">
<intercept-url filters="none" pattern="/javax.faces.resource/**" />
<intercept-url filters="none" pattern="/services/rest-api/1.0/**" />
<intercept-url filters="none" pattern="/preregistered/*"/>
<intercept-url
pattern="/**/*.xhtml"
access="ROLE_NONE_GETS_ACCESS" />
<intercept-url
pattern="/auth/*"
access="ROLE_ANONYMOUS,ROLE_USER"/>
<intercept-url
pattern="/preregistered/*"
access="ROLE_ANONYMOUS,ROLE_USER"/>
<intercept-url
pattern="/registered/*"
access="ROLE_USER"
requires-channel="http"/>
<form-login
login-processing-url="/j_spring_security_check.html"
login-page="/auth/login.html"
default-target-url="/registered/home.html"
authentication-failure-url="/auth/login.html" />
<logout invalidate-session="true"
logout-url="/auth/logout.html"
success-handler-ref="DCLogoutSuccessHandler"/>
<anonymous username="guest" granted-authority="ROLE_ANONYMOUS"/>
<custom-filter after="FORM_LOGIN_FILTER" ref="xmlAuthenticationFilter" />
<session-management session-fixation-protection="none"/>
</http>
<!-- Configure the authentication provider -->
<authentication-manager alias="am">
<authentication-provider user-service-ref="userManager">
<password-encoder ref="passwordEncoder" />
</authentication-provider>
<authentication-provider ref="xmlAuthenticationProvider" />
</authentication-manager>
</beans:beans>
the below filter took care of my situation:
package com.dc.api.service.impl;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
public class CacheControlFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Expires", "Tue, 03 Jul 2001 06:00:00 GMT");
resp.setHeader("Last-Modified", new Date().toString());
resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0");
resp.setHeader("Pragma", "no-cache");
chain.doFilter(request, response);
}
#Override
public void destroy() {}
#Override
public void init(FilterConfig arg0) throws ServletException {}
}
to solve this problem you must add in your security xml config file :
<security:http auto-config="true" use-expressions="true">
<security:headers >
<security:cache-control />
<security:hsts/>
</security:headers>
In spring 3.0.x
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="cacheSeconds" value="0" />
</bean>
In spring 2.5.x
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="cacheSeconds" value="0" />
</bean>
Yes, I used spring-security 3.2.9.RELEASE and simply giving <security:headers /> in one the spring config file like applicationContext.xml file as in the above posts
<security:http
auto-config="true" use-expressions="true">
<security:headers />
</security:http>
so that user won't be able to go to visited other app pages
using browser back and forward buttons after logout.
If you, like me, didn't get it working after using c12's caching filter, and you are using <security:http auto-config="true"> make sure you don't need the auto-config="true" part anymore. It (looks like it) adds http basic authentication which does not handle logging out by protocol! This results in that you can GET your logout URL but hitting the back button will just bring you back since you're not really logged out.

Resources