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
Related
tldr; What's the appropriate way to register either a custom AuthenticationSuccessHandler or configure the SAMLRelayStateSuccessHandler to set the redirect URL Spring Security's SAML extension after a successful authentication?
I am new to Spring and Spring Security, but I've had some success getting the SAML extension to work for a Single Sign-On application. However, I've run into a problem where Okta (the IDP) and my application will loop continuously after successful authentication instead of going to the original URL. My suspicion is that this is caused by the fact that I am using Vaadin as well as Spring and once the Vaadin servlet loads it is doing something funky with or ignoring the Spring filters entirely. This is suggested a possible problem and solution in a blog article on combining filter-based Spring Security with Vaadin where the proposed solution is to register an AuthenticationSuccessHandler.
However, this is not as simple with the Spring Security SAML Extension as it is with, say, a simple form login or OpenID. For example, with a form login registing a success handler is as easy as
http.formLogin().successHandler(handler);
No such option is available with the SAML Extension. However, in another post Vladimír Schäfer (see "Spring saml - how remember request parameter when initiate login on SP, and procesing them after IdP response) suggests changing the AuthenticationSuccessHandler to a SAMLRelayStateSuccessHandler. The documentation suggests that this would be the perfect way to do it. However, I would prefer not to subclass SAMLEntryPoint.
My current SecurityConfiguration is entirely Java-based and follows Matt Raible's article on using Spring and Okta together. The SecurityConfiguration is as follows:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().disable() // Disable Spring's CSRF protection and use Vaadin's.
.authorizeRequests() // Everything else: Do SAML SSO
// Allow everyone to attempt to login
.antMatchers("/root/saml*").permitAll()
// But make sure all other requests are authenticated
.anyRequest().authenticated()
.and()
// Create a completely new session
.sessionManagement().sessionFixation().newSession()
.and()
// Configure the saml properties
.apply(saml())
.serviceProvider()
.keyStore()
.storeFilePath("saml/keystore.jks")
.password(this.password)
.keyname(this.keyAlias)
.keyPassword(this.password)
.and()
.protocol("https")
// Do I need to adjust this localhost piece?
.hostname(String.format("%s:%s", "localhost", this.port))
.basePath("/")
.and()
// Load the metadata from the stored properties
.identityProvider().metadataFilePath(this.metadataUrl);
}
```
What's the best way to register the AuthenticationSuccessHandler or configure the SAMLRelayStateSuccessHandler? Any other thoughts would be greatly appreciated too!
I'm using:
#Bean
public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() {
SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler =
new SavedRequestAwareAuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
super.onAuthenticationSuccess(request, response, authentication);
}
};
successRedirectHandler.setDefaultTargetUrl("/");
return successRedirectHandler;
}
It seems to avoid needing a separate securityContext.xml file.
The solution to registering a custom handler is to configure the Spring SAML extension using the sample securityContext.xml file provided in the Sample program. Full details are available by carefully reading the Spring SAML Extension guide. If you are using Spring Boot, you need to tell it to import the securityContext.xml file by adding an #ImportResource annotation. In my case, I removed all the code from my security configuration class, but left the annotations and added the #ImportResource as follows:
/**
* This class is the main security configuration for working with the
* Single-Sign On SAML backend. All it does it import the security context file
* and configure other annotation-based options. All of the real configuration
* options are in WEB-INF/securityContext.xml.
*
* #author Jay Jay Billings
*
*/
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
#ImportResource("WEB-INF/securityContext.xml")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {}
Once the securityContext.xml file is in place and reconfigure for your application, you can add a custom handler in the successRedirectHandler bean:
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler"
class="com.example.app.customSuccessHandler">
</bean>
That is the answer to my question, but it did not fix my redirect loop problem on its own. The solution to the redirect loop also required 1) fixing the entity base URL in the MetadataGenerator bean, and 2) fixing the security:intercept-url pattern value to account for my modified (non-root) base URL. For information on the latter, see "This webpage has a redirect loop in spring-security application" on this site.
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.
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).
I am using my own MVC implementation and I am not sure, whether the Spring Security isn't designed specifically for the Spring MVC implementation. Is it still okay to use it?
It is not clear to me, which parts of Spring Security I should use and which I don't need to. I suppose I don't need the URL filters as I need to validate the access specifically on the URL parameters in my controllers (for instance to check whether User is the owner of the Article). Something like:
if (request.getUser().isAllowedTo("EditArticle", 27)) {...}
if (request.getUser().isAllowedTo("CategoryManager", 123)) {...}
if (request.getUser().isInRole("Admin")) {...}
I could not find some clear way of users logging in/out programmatically. I've implemented my UserDetails and UserDetailsService classes to handle the users with JPA, but I don't see a way, how I can proceed with login and logout in my controllers.
EDIT:
I don't see a way how to put the <form> in my Freemarker templates - the only way of creating the form I found is with:
<http pattern="/login.htm*" security="none"/>
<form-login login-page="/login.htm" default-target-url="/home.htm"/>
</http>
How can I configure the structure of the login form? Can I create it by myself?
What I would like the most is to be able to handle the login by myself (for example with DWR) and not to use the magic j_security_checks...
It would be great, if I could handle the login request by myself and ask the auth service to login the user by myself (so I could easily use Direct Web Remoting (DWR)):
if (user.username.ok() and user.password.ok()) {
authService.setUser(user);
authService.setLoggedIn(true);
}
Spring security is not limited to spring mvc and can be used with your own framework implementation. It provides handy services for authentication and session management.
Spring is very convenient to use to leverage access by certain URLs but not limited to. You will be able to get which roles the current user has at the moment from the spring context and all the custom info you would include in your UserDetails object. There are number of ways to restrict access to certain actions by certain roles. However the code like if (request.getUser().isAllowedTo("EditArticle", 27)) {...} I think it will be simplier to check by yourself.
Login and logout are done by calling specific urls. For login: /j_spring_security_check. For logout: /j_spring_security_logout.
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.