Cleanest way to call multiple AuthenticationSuccessHandlers? - java

I have a java webapp using Spring 3.1. My Spring security context defines multiple authentication filters, each corresponding to a different authentication path (e.g. username/password vs. Single Sign On). Each auth filter defines its own AuthenticationSuccessHandler. Now, I want to inject 2 additional actions to take upon successful authentication, and they should apply across all authentication types:
set a tracking event code for Google Analytics to use on the front-end
update the user's preferred locale in our database
These could be any actions that you want a hook for, after the user has been successfully authenticated. The important point is that, unlike the regular AuthenticationSuccessHandlers (which are different for each authentication path), they don't forward or redirect the request. So it's safe to call a bunch of them.
Is there a clean way to integrate these additional authentication success "actions", using Spring Web/Security 3.1?
I looked into implementing an ApplicationListener<AuthenticationSuccessEvent>, but my events need to access the request, and all AuthenticationSuccessEvent provides is the Authentication object itself.
I couldn't find a way, so I decided to roll my own proxy:
public class AuthenticationSuccessHandlerProxy implements AuthenticationSuccessHandler {
private List<AuthenticationSuccessHandler> authenticationSuccessHandlers;
public AuthenticationSuccessHandlerProxy(List<AuthenticationSuccessHandler> successHandlers) {
this.authenticationSuccessHandlers = successHandlers;
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
for (AuthenticationSuccessHandler successHandler : this.authenticationSuccessHandlers) {
successHandler.onAuthenticationSuccess(request, response, authentication);
}
}
}

After looking breafly into the source code of AbstractAuthenticationProcessingFilter and all other places where AuthenticationSuccessHandler.onAuthenticationSuccess(...) is called I do not see any possibility to do it using Spring Security.
As a workaround you can try to wrap your success handlers into some AspectJ or AOP pointcut and then apply this pointcut to AuthenticationSuccessHandler.onAuthenticationSuccess(...) execution. Maybe like this you can target all authentication types.

Related

How to set the ignoring URL in a project that uses spring session

Using spring boot to integrate spring session and spring actuator at the same time. Due to the use of prometheus monitoring, it will periodically initiate http requests for health checks and performance monitoring, but each request will generate a new session, due to the use of session sharing. The session is stored in redis, which produces a large number of useless sessions. Is there any better solution at present?
I found a similar problem in github, and the problem is still open, but someone has already proposed a solution in the problem, the link is as follows: Is it possible to exclude some url from the SessionRepositoryFilter.
The sample code use filter set SessionRepositoryFilter.FILTERED.
#Component
#Order(Integer.MIN_VALUE)
public class ExcludeSessionRepositoryFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
FilterChain filterChain) throws ServletException, IOException {
if (/* here goes your logic to exclude the session repository filter, probably depending on the request uri */) {
httpRequest.setAttribute("org.springframework.session.web.http.SessionRepositoryFilter.FILTERED", Boolean.TRUE);
}
filterChain.doFilter(httpRequest, httpResponse);
}
}
Of course, there is another solution to complete the integration of springboot and spring security. After the integration is completed, the SessionCreationPolicy enumeration parameters can be adjusted due to the existence of the SessionManagementConfigurer, and the session creation strategy can be adjusted. Its default value is SessionCreationPolicy.IF_REQUIRED, only after testing After logging in, the session will be synchronized to redis, which is more in line with the concept of session sharing.

How to invoke a Spring Security Provider?

I have a web app that I am migrating to Grails 3.3.9 (from Grails 2.3.11). The current version of the application utilizes Spring Security LDAP for authentication (user sees login form when they try to access site and they type in username and password). The newer version of the application will utilize the Spring Security SAML plugin for authentication (commonly referred to as Single Sign On).
I have the Single Sign On (SSO) working however the SSO Login Page is only accessible when a user is at our office (has a certain IP address). In the cases where the user is not at our office (has an IP address not in our network). I would like the user to have the option to login with the Spring Security LDAP login form.
I'm sort of lost how to do it though. From the info I've gathered I need to define my security provider in application.groovy(I've used the default Spring Security providers as they seem to do the job individually). What I don't understand though is how do I tell Grails which of the two login methods to use per user. In my instance it would be checking the IP address of the user (which I already have the code for), but how do I then say, for example:
if(ipAddress matches internalIPRange) use samlAuthenticationProvider
else{use ldapAuthProvider}
Here is the provider set up in application.groovy
grails.plugin.springsecurity.providerNames = ['samlAuthenticationProvider', 'ldapAuthProvider', 'daoAuthenticationProvider', 'anonymousAuthenticationProvider']
Also I don't know how to actually call the provider manually (something like provider.invoke() if I had to guess).
It appears to be fairly easy to implement.
You extend the ExceptionTranslationFilter like so:
class IpAwareExceptionTranslationFilter extends ExceptionTranslationFilter {
AuthenticationEntryPoint ldapAuthenticationEntryPoint
#Override
void sendStartAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
AuthenticationException reason) throws ServletException, IOException {
SecurityContextHolder.context.authentication = null
requestCache.saveRequest request, response
logger.debug 'Calling Authentication entry point.'
if( isAllowedIpAddress( request ) )
authenticationEntryPoint.commence request, response, reason // default SAML
else
ldapAuthenticationEntryPoint.commence request, response, reason // LDAP
}
}
Then you declare your filter as a bean in resources.groovy:
beans = {
exceptionTranslationFilter( IpAwareExceptionTranslationFilter ){
authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint( '/saml/login' )
ldapAuthenticationEntryPoint = new LoginUrlAuthenticationEntryPoint( '/ldap/login' )
}
}
and it should replace the default exceptionTranslationFilter in the filter chain.

Spring Boot, Session Security

I have some resources that I have in a Spring Boot setup that a user needs to have a session for.
Right now, these endpoints in need of defense all look like this.
#RequestMapping(value = "/SomeEndpoint", method = RequestMethod.POST)
public ResponseEntity<SomePacket> Foot(#RequestBody Dummy dummy,
HttpServletRequest request,
HttpServletResponse response) throws IOException, RestSessionException {
if (session.getAttribute("clientId") == null) {
throw new RestSessionException("No Client");
}
Each.and.every.one.of.them...
What I would like to do is reduce the amount of duplication throughout my codebase with checking for a "clientId" on the session. I've read about WebSecurityConfigurerAdapters, but they seem to revolve more around the fluent interface, and not so much around retrieving session values.
Does anyone know how I can check session properties for certain endpoints in a WebSecurityConfigurerAdapter?
Any help is greatly appreciated.
WebSecurityConfigurerAdapters is implementation of spring security and which provides functionality for authorisation and authentication for your endpoints. For more info about WebSecurityConfigurationAdapter read this
Your requirement can simply achieve by adding filter and applying required logic of checking clientId in doFilter method.

Why spring makes it so hard to use additional login parameters with spring security

In a spring mvc application i need to capture an additional 'location' parameter on login screen and use it for authentication in addition to username. I came across few approaches suggested to achieve this but none of it is straight forward and involves extending and/or implementing number of spring classes and interfaces. I somehow achieved it by extending UsernamePasswordAuthenticationFilter and by retrieving and putting location parameter in session.
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
final Long locationId = Long.parseLong(request.getParameter("locations"));
request.getSession().setAttribute("LOCATION_ID", locationId);
return super.attemptAuthentication(request, response);
}
}
This approach seems like a hack and doesn't seem too elegant. On top of that, now since i am using a custom AuthenticationFilter i have to manually configure this filter by injecting number of dependencies.
#Bean
public CustomAuthenticationFilter customAuthenticationFilter () {
CustomAuthenticationFilter filter= new CustomAuthenticationFilter();
filter.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/login","POST"));
filter.setAuthenticationManager(authenticationManagerBean());
filter.setUsernameParameter("username");
filter.setPasswordParameter("password");
filter.setAuthenticationSuccessHandler(simpleUrlAuthenticationSuccessHandler());
filter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler());
filter.setRememberMeServices(persistentTokenBasedRememberMeServices());
return filter;
}
Eventually, capturing location and using it in authentication works but then it creates new issues if i want to use remember-me feature. I explained the problem here Spring remember-me with extra login parameter.
Using extra login parameters must be a common requirement. Spring framework known for being pluggable and extensible i wish there was a more user friendly way to use additional parameters.
Can anyone please suggest me a better approach here to use extra parameter and get remember-me working as well. Thanks
The classes of spring security are really complicated.
Use AbstractPreAuthenticatedProcessingFilter as your superclass.
Than you can return an Object instead of a String in
getPreAuthenticatedPrincipal
getPreAuthenticatedCredentials
there you read whatever you need from the HttpRequest and return an object that contains all the data you need (as you need it as principal or credential wherever location belongs.). Maybe you create a simple class for the return type.
Than you create a subclass of org.springframework.security.authentication.AuthenticationProvider
.In the method public Authentication authenticate(Authentication authentication)
you can access the objects you returned in AbstractPreAuthenticatedProcessingFilter using authentication.getCredentials() and authentication.getPrincipal()
with this you create one or more new SimpleGrantedAuthority(role), one for each role the user has.Collect them in a List grantedAuthorities;
And finally create a
Authentication auth=new new UsernamePasswordAuthenticationToken(authentication.getPrincipal(),authentication.getCredentials(),grantedAuthorities)
which you return.

Using Spring Security with zero-legged Oauth 1

I am writing an LTI application using Spring boot. LTI applications are basically a plug-in for a learning management system (in this case Canvas) which work by sending an Oauth1 signed POST to my server. The result of this request is displayed to the user inside of an iframe. There is a pre-shared key and secret that the LMS uses to sign the request. If the signature on the POST checks out, I have an authenticated user. I have this part working, partially based on this question.
During the initial request (which comes to the URL "/launch") I can call SecurityContextHolder.getContext().getAuthentication() and use this without problems. My problem is when the user makes another request, say for a picture or by clicking on a link in my content, the SecurityContext object isn't following them. I'm pretty sure I'm not setting up the Spring security filter chain correctly so the SecurityContextPersistenceFilter isn't being hit on subsequent requests. At the end of the day, SecurityContextHolder.getContext().getAuthentication() returns null.
The OAuth signature verification happens in a WebSecurityConfigurerAdapter like so: (again, based on this)
#Configuration
public static class OAuthSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
//spring auto-wiring to set up the
//zeroLeggedOauthProviderProcessingFilter (see linked question)
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/launch")
.addFilterBefore(zeroLeggedOAuthProviderProcessingFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().anyRequest().hasRole("OAUTH")
.and().csrf().disable();
}
}
So this works and creates an authenticated principal and everything. But due to that antMatcher, it only applies to the /launch path.
It seems like it should be simple to add another security configurer adapter that will ensure that all other paths in the application are protected by an authenticated session and in so doing would cause the SecurityContext associated with this user to become available but I have been unable to come up with the magic sauce. The documentation focuses more on standard login form based authentication setups. I'm also kind of new to Spring in general so I'm clearly missing something. I tried this but it causes all other requests to return a 403:
#Configuration
public static class SessionSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().hasRole("OAUTH")
.and().csrf().disable();
}
}

Categories

Resources