Preauthenticate and forward username to default authentication provider - java

I'm new to Spring Security, so forgive me if this is straight forward.
We are in the process of rewriting the UI layer of a legacy web app. We decided that the new UI layer would be based on Spring MVC with Spring Security to handle security and authorization.
I've been looking at setting up security in the new app to mimic what we had in the previous app. In the old app, we basically have two entry points for users:
Internal users are authenticated via HTTP basic authentication which
performs the actual authentication using the clients LDAP server.
This authentication mechanism is configured on a JBoss server, so is
container-managed.
External users logs in via a third-party authentication service that validates the credentials. External user roles are stored in the LDAP server. When the third-party authentication service authenticates the credentials, a username and hardcoded password is used to authenticate them on the JBoss configured security domain so their roles are loaded.
I figured I would try and mimic this functionality in Spring Security but have so far come up short. I use an in-memory authentication provider in place of LDAP in my tests since its easier. I've got http basic authentication working for internal users.
I've tried subclassing AbstractPreAuthenticatedProcessingFilter and supplying the credentials through this, but it does seem to forward the credentials properly to the "default" authentication provider.
<http>
...
<http-basic />
<custom-filter position="PRE_AUTH_FILTER" ref="ExternalLoginFilter" />
</http>
<beans:bean id="ExternalLoginFilter" class="com.foo.ExternalLoginPreAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="internal-user" password="password" authorities="ROLE_USER, ROLE_INTERNAL" />
<user name="external-user" password="password" authorities="ROLE_USER, ROLE_EXTERNAL" />
</user-service>
</authentication-provider>
</authentication-manager>`
Here is my ExternalLoginPreAuthenticationFilter:
public class ExternalLoginPreAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest req) {
return "password";
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest req) {
HttpSession session = req.getSession(false);
if(session != null){
return session.getAttribute("app.external-user.username");
}
return null;
}
}
I also tried setting up a "preAuthenticatedAuthenticationProvider" like some examples propose, but that seems to expect that my ExternalLoginPreAuthenticationFilter has resolved user roles as well.
Any ideas how I can configure Spring MVC to allow the scenario above? Basically, I need to be able to tell Spring Security to perform the login against the default authentication provider with a specific username/password in the least intrusive way, preferably without too many hacks (which the old application uses).
Note on solution: While Ralph's solution seem to work, specifically this portion:
I think using the PreAuthenticatedAuthenticationProvider and set the preAuthenticatedUserDetailsService variable to your in memory AuthenticationUserDetailsService should be the way to go.
However, it also seems to play badly with CSRF protection. When I log in, and is redirected to the homepage, any HTTP POST from this page will fail the CSRF check. A subsequent GET of the homepage before POST'ing fixes the issue, so it seems Spring somehow overwrites the current CSRF token improperly. I found a bug report detailing the problem. Even though it claims to be fixed, I have not been able to work around it. While the bug report links to a workaround in the forum, I have instead used to following workaround, which seems to work.
The trick is to inject the AuthenticationManager into your Controller and do the login yourself:
#Controller
#RequestMapping(value = "/external-login")
public class ExternalLoginController {
private AuthenticationManager authenticationManager;
#Autowired
public ExternalLoginController(AuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
}
// ...
#RequestMapping(method = RequestMethod.POST)
public String login(){
// Do this after third-party authentication service accepts credentials
String username = "external-user"; // or whatever username was authenticated by third-party
UsernamePasswordAuthenticationToken credentials = new UsernamePasswordAuthenticationToken(username, "password");
Authentication auth = authenticationManager.authenticate(credentials);
SecurityContextHolder.getContext().setAuthentication(auth);
return "redirect:/";
}
}

The problem is, that the AuthenticationProvider is selected in the AuthenticationManager by the authentication credential class that is provided (often a subclass of AbstractAuthenticationToken).
Your PreAuthenticationProcessingFilter will create a PreAuthenticatedAuthenticationToken, that is normaly "consumed" by an PreAuthenticatedAuthenticationProvider.
So either:
you register a AuthenticationProvider that "consume" the PreAuthenticatedAuthenticationProvider token, and do what you want, or
you change the PreAuthenticationProcessingFilter to create an other kind of token (for example a UsernamePasswordAuthenticationToken that is "consumed" by the "normal" authentication provider you use (subclass of AbstractUserDetailsAuthenticationProvider))
I think using the PreAuthenticatedAuthenticationProvider and set the preAuthenticatedUserDetailsService variable to your in memory AuthenticationUserDetailsService should be the way to go.

Related

Prevent spring security to authenticate users whom has BadCredentialException via next authentication provider

I config spring security with multiple authentication provider:
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="testUser" password="TestPwd"
authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
<security:authentication-provider
ref="customAuthenticationProvider" />
</security:authentication-manager>
I want to prevent authenticate a user via second provider if user's password was invalid in first provider. if example, if user with username 'testUser' could not authentication via in memory provider(so, user's password was not equal 'TestPwd'), customAuthenticationProvider don't authenticate user again.
So basically, provider manager iterate through all authentication provider and checks authentication. By default, if there is a any error of type AuthenticationException, spring checks for another provider.
But you don't want to check with another provider. To solve this issue, you need to have your own provider manger and override authenticate method.
I believe entire code in overridden method would remain same except here. Here you just need to add break statement.
How it will work?
As you mentioned, you have two provider 1) In memory 2) Custom Authentication provider. Both will have overridden public Authentication authenticate(Authentication authentication) method and this method should throw BadCredentialsException if credential does not match.
So, while iterating through providers(in your custom provider manager), your in memory provider will throw BadCredentialsException and exception would catch here. Since, you have written break, loop will exit and custom provider manager will not go for another provider to check authentication.

How to make an async Spring Authentication Provider

We have some Spring MVC web services which use Spring's support for async servlets. As I understand the concept, this allows for greater scalability as the web container creates fewer threads.
We also use Spring Security to secure our services. Is it possible to make the authentication manager and authentication providers run asynchronously as well?
Our authentication provider needs to access a database or external web service. So there will certainly be some delay in the authentication part of the web request. Without an async authentication manager, it seems that the benefits of async controller methods are reduced.
The Spring Authentication Provider authenticate() method is a synchronous method.
To clarify, I'm not talking about getting Spring credentials from within an async MVC controller. I've found plenty of documentation on that.
Here is how I have my Spring security application context defined currently.
<http auto-config="true" entry-point-ref="authenticationEntryPoint" create-session="never" use-expressions="true">
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')"/>
</http>
<authentication-manager>
<authentication-provider ref="myAuthenticationProvider" />
</authentication-manager>
<beans:bean name="myAuthenticationProvider" class="com.testing.CustomAuthenticationManager"/>
My web controller looks something like the following.
#RequestMapping(value = "{id}", method = RequestMethod.GET)
#ResponseBody
public Callable<ResponseEntity> getData(#PathVariable String id) {
return new Callable<ResponseEntity>() {
#Override
public ResponseEntity call() throws Exception
{
}
}
And of course, I have an implementation of CustomAuthenticationManager.
From my understanding of Spring Security and MVC, this is a crude representation of what Spring is doing.
Spring Security receives a request through a filter
Spring Security gives this to the Authentication Manager, which uses the provided Authentication Provider (CustomAuthenticationManager)
The Authentication Provider authenticates the user and gets his authorities
If the user has the correct authorities, Spring calls my web controller (getData).
When Spring receives a Callable back, Spring calls AsyncContext.start(Runnable).
When the Callable completes, Spring completes the AsyncContext.
What I want to add as well is in step #3.
The Authentication Provider tells Spring (somehow) that it is async.
Spring calls AsyncContext.start(Runnable)
The Authentication Provider takes a while to authenticate, then returns a result
Spring completes the AsyncContext

How to implement Spring's pre-authentication filter?

I'm trying to implement pre-authenicated security in our web application but I'm not sure how to do it correctly. There aren't that many examples out there. And the ones that are seem to have a much simpler setup than ours.
We get our authentication details in a request header as an XML with a firstname, lastname, user ID and an error tag, if any occured.
I'm extending AbstractPreAuthenticatedProcessingFilter and in its getPreAuthenticatedPrincipal() I extract the header, unmarshall it, and do some validation.
Now the questions:
If everything's OK, do I just return my unmarshalled shibboleth from getPreAuthenticatedPrincipal()?
If something's wrong, do I just throw a PreAuthenticatedCredentialsNotFoundException?
What do I return from getPreAuthenticatedCredentials()? Is "N/A" sufficient?
I suppose at some point I have t create an Authentication and a Principal.
Is this a good approach?
Principal dynamicUser = new DynamicUser(rijksregisterNummer);
List<SimpleGrantedAuthority> grantedAuthorities = Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
Authentication authentication = new AnonymousAuthenticationToken(rijksregisterNummer, dynamicUser, grantedAuthorities);
At what point (in which class) do I set it in the Spring Security?
What other classes do I need to extend?
How do I configure the Spring Security configuration XML? Like this? What am I missing?
<http>
<custom-filter position="PRE_AUTH_FILTER" ref="myPreAuthFilter" />
</http>
<bean id="myPreAuthFilter" class="my.package.MyPreAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customAuthenticationProvider"/>
</authentication-manager>
External users go through pre-authentication (using an e-ID and card reader) and then hit our web application. Internal users however, have to authenticate with a uername and password, a normal authentication procedure.
How can I set it up that when there isn't an shibboleth (thus a login from our internal users), I can display a login form?
Lot's of question, I know. I hope you can guide me through.
This is an old question but still relevant. As said, there was many questions. What caught my eye was the observation that there are not many example implementations.
I've been playing with an implementation. You can find it here: https://github.com/klaalo/reqTokenAuth.
It is easy to mix and match with authentication methods on WebSecurityConfigurerAdapter so that you can have traditional form based authentication for legacy users.
The implementation is based on the setting where Apache mod_auth_openidc sits in front of the application. However, the implementation should work fine also with Shibboleth SP.
I didn't quite catch your sentiment about sending authentication details in HTTP Request headers as XML. Shibboleth SP is about SAML-authentication. You should leave the details about authentication for the SAML SP and only enjoy the benefits of readily authenticated user in your application. There's no need for unmarshalling the XML, Shibboleth does that for you. You get user details as clean Strings representing SAML attribute values in HTTP Headers or HttpServletRequest attributes (when Tomcat/AJP is used).

How to process a form login using Spring Security / Spring MVC

Simple question, I just need a pointer in the right direction:
I have a simple Spring MVC/Spring Security webapp. Initially I set up Spring Security so that the default login page shows and authenticates properly (I implemented the UserDetailsService with the DaoAuthenticationProvider to do this).
Next step: replace the default spring login page with my login page and post the credentials.
But what do I do with the submitted login credentials? I assume I post the form to a controller, verify the credentials, but I'm not clear what the right step is after that. E.g.:
Am I calling a method of AuthenticationManager?
Do I need to define a bean for this?
Is there an interface/service I need to implement like an AuthenticationEntryPoint or something?
I've hit the docs 3 times over and don't quite follow them. I know this is dirt simple, so I just need to hear how the process should flow.
I'll add a clarifying answer for anyone reading this in the future:
When you define the tag in spring security it will handle the login for you, I'll go over how it works in detail (wish it were this detailed in the docs):
<security:http auto-config="true">
<security:form-login login-page="/login"
login-processing-url="/postlogin"
default-target-url="/myaccount"
authentication-failure-url="/login?loginError=true" />
<security:logout logout-url="/logout" />
</security:http>
The login-page is the url of the login page. You should have a controller (or static HTML page) that serves this page, it's your pretty login form.
The login-processing-url is a URL which the form-login component handles. It's as if the form-login component implemented its own controller for this page. You should post your form to this page. You also need to know to name your username/password parameters "j_username" and "j_login"
Beyond this, and the rest of the reasonably obvious options above, you should have implemented a UserDetailsService - that is, create a class and implement the interface UserDetailsService which gets, and returns, a UserDetails object (username/password) for a given username - and provide that UserDetails object with the rest of the security configuration:
<security:authentication-manager>
<security:authentication-provider ref="daoAuthenticationProvider" />
</security:authentication-manager>
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider" >
<property name="userDetailsService" ref="myAuthorizationService" />
</bean>
Spring Security reference documentation outlines the basic processing flow in 5.4 Authentication in a Web Application. There is point #6:
Next the server will decide whether or not the presented credentials are valid. If they're valid, the next step will happen. If they're invalid, usually your browser will be asked to try again (so you return to step two above).
...
Spring Security has distinct classes responsible for most of the steps described above. The main participants (in the order that they are used) are the ExceptionTranslationFilter, an AuthenticationEntryPoint and an “authentication mechanism”, which is responsible for calling the AuthenticationManager which we saw in the previous section.
I have to admit, the documentation here is a bit confusing so I will give you some more pointers - the "authentication mechanism" mentioned here is the thing you are after, it is responsible for processing the credentials that the browser is sending.
As the details of attaching the credentials to HTTP request(s) vary greatly among different authentication methods (form data vs. plain headers vs. digest headers), there is no common "authentication mechanism" - instead, each method implements its own mechanism and in the case of web-based authentication, it is typically a special filter that you have to configure in web.xml.
In your case, you are most probably interested in UsernamePasswordAuthenticationFilter - this is used for processing basic form-based login information. The contract between your custom login form and the filter is the URL (where form is posted) + username and password field names:
The login form simply contains j_username and j_password input fields, and posts to the URL that is monitored by the filter (by default this is /j_spring_security_check).
See the posting by limc in response to Ritesh's answer at Configuring Spring Security 3.x to have multiple entry points Look at the sections titled:
UPDATE 01-29-2011 - #Ritesh's technique
UPDATE - SOLUTION to #Ritesh's technique
It is a concise, advanced good example of how you can customize the login process in Spring Security
<security:http auto-config="true">
<security:form-login login-page="/login"
login-processing-url="/postlogin"
default-target-url="/myaccount"
authentication-failure-url="/login?loginError=true" />
<security:logout logout-url="/logout" />
</security:http>
login-page is the login form URL
login-processing-url is the url where login form is submitted. Spring will give a self call the authentication-manager mapped by you in security.xml file.
Process
You need to write a class that implements org.springframework.security.authentication.AuthenticationProvider with overriden method Authentication authenticate(Authentication authentication)
Second class that extends org.springframework.security.core.userdetails.UserDetailsService with a overridden method loadUserByUsername(String username)
The second class form where you can call your DAO and validate user with database fetching username and password.
In Authentication authenticate(Authentication authentication) you will compare the password and return success failure your are done.
We are talking about the same problem more and less, this is my approach let's see what people will say.
If you're using a JDBC accessible database, then you could use the following authentication-provider and avoid creating a custom one. It cuts down the code required to 9 lines of XML:
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username,password from users where username=?" authorities-by-username-query="select u.username, r.authority from users u, roles r where u.userid = r.userid and u.username =?" />
</authentication-provider>
You can then setup your dataSource as follows
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/DB_NAME" />
<property name="username" value="root" />
<property name="password" value="password" />
</bean>
Have a look at this post: http://codehustler.org/blog/spring-security-tutorial-form-login/
It covers everything you need to know about customising Spring Security form-login.

Spring - Call custom-authentication-provider from a controller

I have a custom-authentication-provider defined in my Spring Security configuration. This class implements AuthenticationProvider, and I can successfully log in using the form defined on my page. The issue is I want to call this class not just on the login page, but from the registration page as well.
The registration page uses a different command class and collects more information than the login form. Right now, when the user registers, I call the appropriate controller, add the record to the database and they can then log in but they aren't logged in automatically. As they've just given me their user name/password on the registration page, can I then pass this to the custom AuthenticationProvider class so they are also logged in?
I've tried creating an org.springframework.security.Authentication class in the registration controller and calling the authenticate method on my customer AuthenticationProvider class, and this doesn't error out, but the user isn't logged in. Do I have to call a method higher in the Spring Security filter chain to accomplish this? Should I redirect the controller to the j_spring_security_check URL? If so, how would I pass the username/password?
You need to put the result of AuthenticationProvider.authenticate() into SecurityContext (obtained from SecurityContextHolder).
Also be aware of AuthenticationSuccessEvent - if your application rely on this event (some Spring Security features may use it, too), you should publish it (you can obtain the default AuthenticationEventPublisher via autowiring). It may be useful to wrap your authentication provider with ProviderManager, it publishes the event automatically using the given publisher.
The problem you are having is that although you have successfully authenticated the user you have not stored the result of this authentication in the user's SecurityContext. In a web application this is a ThreadLocal object which the SecurityContextPersistenceFilter will use to store the user's credentials in the HTTPSession
You should also avoid authenticating directly with your custom authentication provider if you can. Your xml configuration should contain an AuthenticationManager which your custom authentication provider has been wired into. For example,
<bean id="customAuthenticationProvider"
class="com.foo.CustomAuthenticationProvider">
<property name="accountService" ref="accountService"/>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="customAuthenticationProvider"/>
</security:authentication-manager>
If you wire the authenticationManager into your registration service and authenticate using that it will additionally,
allow you to swap in/out additional authentication providers at later points
publish the authentication result to other parts of the Spring Security framework (eg success/failure Exception handling code)
Our registration service does this as follows
final UsernamePasswordAuthenticationToken authRequest = new
UsernamePasswordAuthenticationToken(username, password);
final Authentication authentication =
authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
We also optionally store the authentication result at this point in a remember-me cookie using the onLoginSuccess() method of TokenBasedRememberMeServices.

Categories