Our application used to have only one possibility to log in: username and password. Once a new user logged into the application, their session would appear in Spring Security's SessionRegistry.
Now I'm implementing SAML support with the help of Spring SAML. I oriented the setup heavily towards the sample application's configuration. It all works fine. However I noticed that users that log in via SAML don't get their session added to the SessionRegistry.
The usual context file for form based authentication contains the following:
<session-management
invalid-session-url="/login"
session-fixation-protection="newSession"
session-authentication-error-url="/login?invalid_session=1">
<concurrency-control
max-sessions="1"
error-if-maximum-exceeded="false"
session-registry-alias="springSessionRegistry"/>
</session-management>
In my http element for the SAML configuration I added the same. This created a new SessionRegistry but it did not contain anything. I also tried
<concurrency-control session-registry-ref="springSessionRegistry"/>
but this did not contain any SAML authenticated sessions either.
So how can I access SAML sessions?
The problem is that bean definition parsers of Spring Security only automatically link beans created based on the session-management and concurrency-control to the authentication processors included in core Spring Security modules. This means, that SAMLProcessingFilter.setSessionAuthenticationStrategy() isn't called.
You should be able to get it working by declaring the samlWebSSOProcessingFilter bean in the following way (which refers to the concurrency bean automatically created by the concurrency-control element):
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
<property name="sessionAuthenticationStrategy" ref="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy#0"/>
</bean>
As I said in my comment of Vladimir's answer, my problem was that I was adding only a ConcurrentSessionControlAuthenticationStrategy to the samlWebSSOProcessingFilter, and because of that, the new logged users were not registered in the SessionRegistry of Spring Security.
To fix that, I created a CompositeSessionAuthenticationStrategy like this:
#Bean
public SAMLProcessingFilter samlWebSSOProcessingFilter() throws Exception {
SAMLProcessingFilter samlWebSSOProcessingFilter = new SAMLProcessingFilter();
samlWebSSOProcessingFilter.setAuthenticationManager(authenticationManager());
samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
samlWebSSOProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
samlWebSSOProcessingFilter.setSessionAuthenticationStrategy(authStrategy());
return samlWebSSOProcessingFilter;
}
/**
* #return the strategy for the SAML authentication: ConcurrentSessionControl (max 1 session per user)
* + RegisterSessionAuthStrat (otherwise, the session of the users are not tracked when they authenticate with SAML)
*
* #author Cyril Gambis
* #date 27 juil. 2020
*/
private CompositeSessionAuthenticationStrategy authStrategy() {
List<SessionAuthenticationStrategy> strategies = new ArrayList<>();
ConcurrentSessionControlAuthenticationStrategy concurrentStrategy = new ConcurrentSessionControlAuthenticationStrategy(this.sessionRegistry);
concurrentStrategy.setExceptionIfMaximumExceeded(false);
RegisterSessionAuthenticationStrategy registerStrategy = new RegisterSessionAuthenticationStrategy(this.sessionRegistry);
strategies.add(concurrentStrategy);
strategies.add(registerStrategy);
CompositeSessionAuthenticationStrategy compositeStrategy = new CompositeSessionAuthenticationStrategy(strategies);
return compositeStrategy;
}
Now, the new users are correctly registered.
Related
I have configured timeouts for the HTTP Outbound Gateway providing a reference to a ClientHttpRequestFactory bean using the request-factory attribute:
<int-http:outbound-gateway request-channel="channelGetByCustomer"
request-factory="requestFactoryGetByCustomer"
reply-channel="jsonToObjectChannel" url="${getbycustomer.endpoint.url}"
http-method="GET" expected-response-type="com.mbracero.integration.dto.Item[]">
<int-http:uri-variable name="customerid" expression="payload.customerid"/>
</int-http:outbound-gateway>
<beans:bean id="requestFactoryGetByCustomer" class="org.springframework.http.client.SimpleClientHttpRequestFactory">
<beans:property name="connectTimeout" value="${getbycustomer.timeout}"/>
<beans:property name="readTimeout" value="${getbycustomer.timeout}"/>
</beans:bean>
But I want to load these attributes dynamically from DDBB (or programmatically) and not from Spring initial boot.
How can I do this?
Programmatically you can do that just from any your service injecting that requestFactoryGetByCustomer bean and using its setters:
#Autowired
private SimpleClientHttpRequestFactory requestFactoryGetByCustomer;
....
this.requestFactoryGetByCustomer.setConnectTimeout(30_000);
To read those options from the DB and populate them to the bean definition using Spring Container features (e.g. Property Placeholder like in your current case) you should take a look to the Commons Configuration framework and populate Properties object from the DB SELECT.
I am new to Vaadin and Spring security. Last few days I was googling for the right example how to integrate spring security into vaadin application (not the servlet) so that the users can authenticate themselves using LoginForm vaadin component. Here is the code snippet that I used inside onlogin event:
login = new LoginForm();
login.addListener(new LoginForm.LoginListener() {
private static final long serialVersionUID = 1L;
#Override
public void onLogin(LoginEvent event) {
if (event.getLoginParameter("username").isEmpty() || event.getLoginParameter("password").isEmpty()) {
getWindow().showNotification("Please enter username and/or password", Notification.TYPE_ERROR_MESSAGE);
} else {
SpringContextHelper helper = new SpringContextHelper(getApplication());
authenticationManager = (ProviderManager)helper.getBean("authenticationManager");
try {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(event.getLoginParameter("username"), event.getLoginParameter("password"));
Authentication authentication = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
authentication.getDetails();
} catch (Exception e) {
getWindow().showNotification(e.getMessage(), Notification.TYPE_ERROR_MESSAGE);
}
}
}
});
When I try to login with my credentials that I described in applicationContext.xml file I got an error Null Pointer exception at this line:
authenticationManager = (ProviderManager)helper.getBean("authenticationManager");
I know that the error is thrown by the getBean("authenticationManager"); method because it can't locate "**authenticationManager"** bean. The question would be: what is the "**authenticationManager"** in this context. Is it a class or bean that implements some kind of Spring security framework interface with special methods. Does anyone could provide an example of such bean (class, pojo and etc.). I descriped listeners and context-params in my web.xml as I found on the examples in the internet.
WEB.XML
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
Also I am providing a applicationContext.xml in which I describe authentication manager with plain username and password.
APPLICATIONCONTEXT.XML
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login />
<logout />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="userrrr" password="passsssword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="otheruser" password="otherpasss" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
So In short. Does anyone could provide me a working bean example, for my case, which can authenticate plain usernames and passwords that are described in applicationContext.xml file. If it helps I am using Vaadin 6, Maven, Hibernate, GlassFish and Sping security 3. I will greatly appreciate help because I am working on this issue for the last three days.
There are plenty examples on the internet but they are all unfinished, unclear, uses Vaadin application servelts or jsp login forms (not the application) and different techniques.
I choosed the way that is described in official vaadin wiki https://vaadin.com/wiki/-/wiki/Main/Spring%20Integration?p%255Fr%255Fp%255F185834411%255Ftitle=Spring%2520Integration. But it is so poor and describes spring framework integration not security.
I found another example which looked me perfect Spring Security + Vaadin: How to create custom non-JSP login form? . But there I can't find detailed info about authenticationManager.
Also here is another nice example https://vaadin.com/forum/-/message_boards/view_message/373038. But it is using HttpServletRequest and HttpServletResponse for passing authetication details. I know that vaadin application class can implement HttpServletRequestListener interface with onRequestStart and onRequestEnd methods but how I can pass/use these requests to/with onLogin event.
So dear JAVA masters please help for a newbie JAVA JEDI programmer to choose a right way:)
Spring Context helper class
public class SpringContextHelper {
private ApplicationContext context;
public SpringContextHelper(Application application) {
ServletContext servletContext = ((WebApplicationContext) application.getContext()).getHttpSession().getServletContext();
context = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
}
public Object getBean(final String beanRef) {
return context.getBean(beanRef);
}
}
Try org.springframework.security.authentication.AuthenticationManager interface instead of ProviderManager:
authenticationManager = (AuthenticationManager)helper.getBean("authenticationManager");
EDIT. Replace alias="authenticationManager" by id="authenticationManager in your conf.
This is my scenario:
a web-app perform a sort-of SSO for many applications
logged-in user than click on a link and the app makes a post with user informations (name, pwd [useless], roles) toward the proper application
I am implementing SpringSecurity on one of these application to benefit from its power (authorities in session, methods provided by its classes, etc)
So, I need to develop a custom filter - I guess - that is able to retrieve user informations from request, retrieve from database, through a custom DetailsUserService, further information about the user (email, etc...) and then perform authentication of that user, according to the role retrieved from the request.
I was looking at Pre-Authentication filters, but I'm not sure that it is the right choice. It seems that those object are expected to be used when the principal is already in session, put by some previous authentication machanism (is it right?).
I think that, once identified the correct filter, I should need to perform within something like:
GrantedAuthority[] ga= new GrantedAuthority[1];
ga[0] = new GrantedAuthorityImpl(myUser.getRole());
SecurityContext sc = SecurityContextHolder.getContext();
Authentication a = new UsernamePasswordAuthenticationToken(userName, userPwd, ga);
a = authenticationManager.authenticate(a);
sc.setAuthentication(a);
Is it the proper direction to solve my problem? Do you have suggestions to help me find what's missing?
Thank you all,
Luca
ADDITION:
Hi Xearxess! Sorry to bother you again but it seems that the translation of your code according to SpringSecurity 2.0.4 is more difficult than I thought :S The problem is the XML... I tried different configuration but I ran always into namespace problems, missing attributes, etc...
<?xml version="1.0" encoding="UTF-8"?>
<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-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
<security:http>
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:logout logout-url="/logout" logout-success-url="http://milan-ias-vs.usersad.everis.int/DMTest/" invalidate-session="true" />
<security:custom-filter position="PRE_AUTH_FILTER" ref="preAuthenticatedProcessingFilter" />
</security:http>
<bean id="preAuthenticatedProcessingFilter" class="it.novartis.ram.authentication.PreAuthenticatedProcessingFilter">
<custom-filter position="PRE_AUTH_FILTER"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean class="it.novartis.ram.authentication.PreAuthenticatedUserDetailsService" />
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>
</beans>
The 2 rows referencing CUSTOM-FILTER element are two different tries, both of them signed as error. How can I specify the position of my filter as a property?
Also the authentication provider reference on auth manager definition is marked as error. I think that I need to specify it like a property too, right?
Hope you can give me the last push ;)
Thank you again,
Luca
For sake of completeness, in Spring Security 4 things are slightly changed. For example, the Java configuration is highly recommended. In this way, it's easier to integrate with Spring Boot.
It follows the Java Configuration that is equivalent to the XML configuration given in the above answers.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(customAuthFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.authenticationProvider(preauthAuthProvider())
.authorizeRequests()
.anyRequest().authenticated();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(preauthAuthProvider());
}
#Bean
public PreAuthenticatedAuthenticationProvider preauthAuthProvider() {
PreAuthenticatedAuthenticationProvider preauthAuthProvider =
new PreAuthenticatedAuthenticationProvider();
preauthAuthProvider.setPreAuthenticatedUserDetailsService(
userDetailsServiceWrapper());
return preauthAuthProvider;
}
#Bean
public OnlyRolesPreAuthenticatedUserDetailsService userDetailsServiceWrapper() {
OnlyRolesPreAuthenticatedUserDetailsService service =
new MyPreAuthenticatedUserDetailsService();
return service;
}
#Bean
public MyPreAuthenticatedProcessingFilter customAuthFilter() throws Exception {
MyPreAuthenticatedProcessingFilter filter = new MyPreAuthenticatedProcessingFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
}
I think that the above code is worth, because examples in internet are very basic and the Spring documentation lacks of such details.
Yes, Pre-Authentication Scenarios are exactly what you are looking for.
It seems that those object are expected to be used when the principal
is already in session, put by some previous authentication machanism
(is it right?).
Not really, you can use Pre-Authentication to create PreAuthenticatedAuthenticationToken from request, as you want. Just do few things I described in another question.
First extend AbstractPreAuthenticatedProcessingFilter to obtain username and roles from request:
public class MyPreAuthenticatedProcessingFilter
extends AbstractPreAuthenticatedProcessingFilter {
public MyPreAuthenticatedProcessingFilter(
AuthenticationManager authenticationManager) {
setAuthenticationDetailsSource(new MyAuthenticationDetailsSource());
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return "Anonymous";
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
public static class MyAuthenticationDetailsSource implements
AuthenticationDetailsSource<HttpServletRequest, MySessionUserDetails> {
// roles probably should be encrypted somehow
static final String ROLES_PARAMETER = "pre_auth_roles";
#Override
public MySessionUserDetails buildDetails(HttpServletRequest req) {
// create container for pre-auth data
return new MySessionUserDetails(req.getParameter(ROLES_PARAMETER));
}
}
}
MySessionUserDetails class will split spring with roles to List of SimpleGrantedAuthority or any other GrantedAuthority implementation. Also, List is recommended and superior to GrantedAuthority[].
Second, implement AuthenticationUserDetailsService:
public class MyPreAuthenticatedUserDetailsService implements
AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
#Override
public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token)
throws UsernameNotFoundException {
MySessionUserDetails sessionUserDetails =
(MySessionUserDetails) token.getDetails();
List<GrantedAuthority> authorities = sessionUserDetails.getAuthorities();
return new User(token.getName(), "N/A", true, true, true, true, authorities);
}
}
Then in your XML connect blocks together:
<security:http use-expressions="true">
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:custom-filter position="PRE_AUTH_FILTER"
ref="myPreAuthenticationFilter" />
</security:http>
<bean id="myPreAuthenticationFilter"
class="com.example.MyPreAuthenticatedProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean class="com.example.MyPreAuthenticatedUserDetailsService" />
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>
And voila! You should have authenticated User principal to use in your application.
Code I written here requires Spring Security 3.1 which I strongly recommend if you're about to using it (it does requrire Spring 3.0.7+). Also, Spring Security reference manual is your friend!
For an existing working app, I want to provide a secondary AuthenticationProvider, probably with a DaoAuthenticationProvider. Let's say it's for authenticating a "back up" password, or a prior password that was changed due to strict password policies and the user forgot the new password. ;-)
For proof of concept, what would the implementation look like for this secondaryAuthenticationProvider that will always authenticate the user regardless of the incoming credentials? (something that returns an authenticated Authentication object)
Which one of the MANY org.springframework.security.providers & subpackage classes and methods should I look at?
Example config:
<bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<ref local="secondaryAuthenticationProvider"/> <!-- new AuthProv -->
<ref local="rememberMeAuthenticationProvider"/>
</list>
</property>
</bean>
If you have only one alternative password, you can declare a second DaoAuthenticationProvider backed by a special UserDetailsService, which will produce UserDetails with that alternative password.
Otherwise, you can create a custom AuthenticationProvider. Credentials check in DaoAuthenticationProvider occurs in additionalAuthenticationChecks(), so if you want to change that logic you can create a subclass of DaoAuthenticationProvider and override this method with your implementation.
For example, if you want to authenticate the user regardless of its credentials, you can override this method with an empty implementation.
Sounds to me like you should just create your own UserDetailsService that has this behavior - that would be by far the easiest way to do it.
Typically, when you declare different "<authentication-provider>" for your application (webapp in my case), Spring Security takes care of invoking providers one after another, incase of failure. So, say I have DatabaseAuthenticationProvider and LDAPAuthenticationProvider with DatabaseAuthenticationProvider declared first in the config file, at runtime, DatabaseAuthenticationProvider is invoked first and if authentication fails, LDAPAuthentication is tried. This is cool - However, what I need is a runtime switch.
I would like to have an option of chosing between these two approaches (database based authentication / ldap based authentication) and somehow swith the implementation based on thsi global setting.
How do I do it? Is it even possible with Spring-Security?
I will leave how to inject your own custom authentication provider to the other myriad of examples from Googleland and here on StackOverflow. It looks like it has to do with marking a particular bean with the xml. But hopefully I can fill in some of the other details for you.
So you've defined the class somewhat like above and I'll add more of the details that you'll need for Spring (i.e. merge the stuff from above as well.
public class SwitchingAuthenticationProvider implements AuthenticationProvider
{
....
public List<AuthenticationProvider> getProviders() { return delegateList; }
public void setProviders(List<AuthenticationProvider> providers) {
this.delegateList = providers;
}
....
}
This will allow you to inject a host of providers using spring:
<bean id="customAuthProvider1" class=".....CustomProvider1"> ... </bean>
<bean id="customAuthProvider2" class=".....CustomProvider2"> ... </bean>
...
<bean id="customAuthProviderX" class=".....CustomProviderX"> ... </bean>
<bean id="authenticationProvider" class="....SwitchingAuthenticationProvider">
<security:custom-authentication-provider/>
<!-- using property injection (get/setProviders) in the bean class -->
<property name="providers">
<list>
<ref local="customAuthProvider1"/> <!-- Ref of 1st authenticator -->
<ref local="customAuthProvider2"/> <!-- Ref of 2nd authenticator -->
...
<ref local="customAuthProviderX"/> <!-- and so on for more -->
</list>
</property>
</bean>
In the end how you populate the providers could be any means of getting the delegator a collection of providers. How they map up to which one to use is up to you. The collection could be a named mapped, based on the current state of the delegator. It could be a list of more than one to try. It could be two properties, "get/setPrimary" and "get/setSecondary" for fail-over like functionality. Once you have the delegator injected the possibilities are up to you.
Let me know if this isn't answering your question.
How about writing a delegating AuthenticationProvider that knows how to access your runtime switch and the actual instances of Database/LDAP AuthenticationProvider.
I'm thinking of something like:
public class SwitchingAuthenticationProvider implements AuthenticationProvider
{
private List<AuthenticationProvider> delegateList;
private int selectedProvider;
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException
{
AuthenticationProvider delegateTo = delegateList.get(selectedProvider);
return delegateTo.authenticate(authentication);
}
....
}