Recording logins with Spring Security - spring-security

I want to log every login in my web application. I was able to access the logins which take place through UsernamePasswordAuthenticationFilter but I don't know how to log users who log in using remember-me functionality. I tried overriding the
createSuccessfulAuthentication(HttpServletRequest request, UserDetails user)
of TokenBasedRememberMeServices, but then logouts are recorded too, because the remember-me service re-authenticates the user.

The best way of logging authentication success and failures is to use a Spring ApplicationListener.
Spring Security publishes various events for authentication success and failure which you can listen for. Events are also published when access is denied to a resource.
You can look at LoggerListener as an example. Start by adding one of those to your application context and it will automatically log authentication events at warn level.
Regarding remember-me logins, if you logout and then access the site immediately afterwards, and are re-authenticated using a remember-me cookie, then technically that is the same as any other remember-me authentication, so there's not much you can do about it.
However, if your logout success URL is passing through the remember-me filter, and that is how the new session is being created (without any additional action from the user), then simply omit it that page from the security filter chain.

For logging each sucessful login i think best way is to create LoginSucessHandler and specify authentication-success-handler for normal login as well as remember-me. i have done this with below code and configuration.
#Service
public class LoginSucessHandler extends
SavedRequestAwareAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
User user = (User) authentication.getPrincipal();
// record login success of user
super.onAuthenticationSuccess(request, response, authentication);
}
}
<http auto-config="true" use-expressions="true">
<form-login login-page="/login"
authentication-failure-url="/login.hst?error=true"
**authentication-success-handler-ref="loginSucessHandler"** />
<logout invalidate-session="true" logout-success-url="/home"
logout-url="/logout" />
<remember-me key="jbcp" **authentication-success-handler-ref="loginSucessHandler"**/>
<session-management>
<concurrency-control max-sessions="1" />
</session-management>
</http>

I think in your case will help solution when you will use your custom filter, which will intercept every request to your application. In this filter you can log username for every request.
Here I described how to add your custom filter. You just need to change functionality to what you want. And don't forhet to put your filter after security filter chain in web.xml.

Related

Oauth 1.0a consumer code equesting an access token twice

I've setup a consumer app, and most of the oauth workflow looks correct, but for some reason after the callback url is invoked by the provider, it tries to get an access token TWICE. The first time works
http://localhost:8080/app/ws/oauth/token
[OAuth oauth_consumer_key="itd79n64zlwv5hhv", oauth_nonce="cac26978-c36c-4a8b-8f3e-3e779ff927ab", oauth_signature="5c8BM9qQoijXC2f5IXpQGtSQsys%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1458938403", oauth_token="5451cf20-7eed-4797-819c-ee2316981654", oauth_verifier="c56de555-79df-455e-ab87-f5f11b953fef", oauth_version="1.0"]
response is a 200, payload includes oauth_token=a95d6305-4261-4c1d-a9b0-43411a0c2f2c&oauth_token_secret=573702d2-70ca-412c-84e5-868e9ee07169
but then, it calls the URL again.
http://localhost:8080/app/ws/oauth/token
[OAuth oauth_consumer_key="itd79n64zlwv5hhv", oauth_nonce="6c013ef9-2f3c-49dd-84fb-97db73b5fb39", oauth_signature="5RTQE5XtcqUwEFVvYQjExhH1eio%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1458938403", oauth_token="5451cf20-7eed-4797-819c-ee2316981654", oauth_verifier="c56de555-79df-455e-ab87-f5f11b953fef", oauth_version="1.0"
which causes an exception on the server since the request token has been removed and the access token has already been issued.
When stepping through the code, I can see that the OAuthConsumerContextFilter stores the access token fine after the first call.
Somehow the filter chain ends up bring it back to readResource in CoreOAuthConsumerSupport with the request token.
I built the consumer app using spring-boot.
from: applicationContext.xml
<bean id="oscarService" class="com.mdumontier.oscar.labline.service.OscarService">
<property name="oscarRestTemplate">
<bean class="org.springframework.security.oauth.consumer.client.OAuthRestTemplate">
<constructor-arg ref="oscar" />
</bean>
</property>
</bean>
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="marissa" password="wombat" authorities="ROLE_USER" />
<security:user name="sam" password="kangaroo" authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
<security:http auto-config='true' >
</security:http>
<oauth:consumer resource-details-service-ref="resourceDetails" oauth-failure-page="/oauth_error.jsp">
<oauth:url pattern="/oscar/**" resources="oscar"/>
</oauth:consumer>
<oauth:resource-details-service id="resourceDetails">
<oauth:resource id="oscar"
key="itd79n64zlwv5hhv"
secret="d3psvmrn8k1xws9x"
request-token-url="http://localhost:8080/app/ws/oauth/initiate"
user-authorization-url="http://localhost:8080/app/ws/oauth/authorize"
access-token-url="http://localhost:8080/app/ws/oauth/token"/>
</oauth:resource-details-service>
Spring Boot automatically registers any Beans which implement Filter in the main application filter chain. See: https://stackoverflow.com/a/28428154 for a bit more detail.
The oauth:consumer helper registers both OAuth filters as beans, and seems to not have been updated in a while. I couldn't even get the XML config to work properly under the latest Spring Boot. Anyway, this means that both will be run twice, and in the case of the OAuthConsumerContextFilter this is destructive since it will run outside the security sub-chain and fail every time.
To fix this you have two options.
One, hint to Spring Boot to avoid this behavior by providing a FilterRegistrationBean for each filter it's automatically picking up, like so:
#Bean
public FilterRegistrationBean registration(OAuthConsumerContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
Two, bypass the XML config entirely and use Java config. I've posted a complete working code sample of getting on OAuth 1 consumer in Spring Boot using Java config in this answer: https://stackoverflow.com/a/42143001/2848158
Within the Java config, you would have to either repeat the FilterRegistrationBean trick, or just not register those filters as beans in the first place but rather create and register instances directly with the Security filter chain.

Spring Security, programmatic login for json restful web service

I am using spring 4.2.1 with spring security 4.0.2
On login, I need to return a json object tree to the client, containing the cached data it requires for the session.
So I've added a the following method:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public #ResponseBody ServerResponse<?> login(#RequestBody LoginRequest loginRequest, HttpServletRequest request, HttpServletResponse response) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
Authentication result = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(result);
Object data = null; // Do stuff here
return new ServerResponse<>(data);
}
My spring security config:
<ss:http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint">
<ss:anonymous enabled="false" />
<!-- this is enabled by default in spring 4 -->
<ss:csrf disabled="true" />
<ss:custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
<ss:session-management session-authentication-strategy-ref="sas" />
<ss:port-mappings>
<ss:port-mapping http="8080" https="8443" />
</ss:port-mappings>
<ss:intercept-url pattern="/app/logi**" access="permitAll()" />
<ss:intercept-url pattern="/app/logou**" access="permitAll()" />
<ss:intercept-url pattern="/app/**" access="hasAuthority('user')" />
<ss:intercept-url pattern="/www/**" access="hasAuthority('user')" />
</ss:http>
All the pages I find regarding a programmatic login confirm that what I am doing is fine.
However, when I try and call another web service method later, I get 403 as the client is not logged in.
I read some vague references to having to use a spring filter, but I am not sure how I would get the filter to return the json tree to the client after successful login.
Any suggestions or links to an example on how to do this would be much appreciated.
Thanks
Sooo it turns out the problem was that I was doing Cross Origin Resource Sharing and the browser was not sending the cookie across with the next request.
Basically I was calling the server from html on the file system (with origin file://)
I was handling options calls, but I was not sending back
Access-Control-Allow-Credentials true
headers in the responses and I had to configure angular to send the cookie by passing the flag
withCredentials: true
in the config object to $http.post

LDAP authentication without managerDN and manager password

I am writing an application in Java Spring framework to perform Active Directory LDAP authentication.
I am succeeding in connecting to my organization LDAP.
Here is the configuration settings:Spring-security.xml
<!-- This is where we configure Spring-Security -->
<security:http auto-config="true" use-expressions="true"
access-denied-page="/oops">
<security:intercept-url pattern="/auth/*"
access="isAuthenticated()" />
<security:logout invalidate-session="true"
logout-success-url="/" logout-url="/logout" />
</security:http>
<security:authentication-manager>
<security:ldap-authentication-provider
user-search-filter="(&(sAMAccountname={0})(objectCategory=user))"
user-search-base="DC=am, DC=example, DC=com" group-search-filter="(&(sAMAccountname={0})(objectCategory=group))"
group-search-base="DC=am, DC=example, DC=com">
</security:ldap-authentication-provider>
</security:authentication-manager>
<security:ldap-server url="ldaps://myserver.am.example.com:4567"
manager-dn="CN=Johnson \, Mitchell, OU=San Francisco,DC=am,DC=example,DC=com"
manager-password="sdvsdvsvs" />
My question here is that,is there any way to authenticate LDAP without supplying manager-dn and manager-password in security:ldap-server tag.
Please provide a solution to this.Thanks in advance.
Yes it is possible: you can let the user who is actualy logging in connecting to the LDAP himself to test his credential and fetch its userdata.
AuthenticationManager configuration:
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(domain, url, rootDn);
activeDirectoryLdapAuthenticationProvider.setSearchFilter(searchFilter);
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider);
}
Spring security does two things:
Let the user log in with his username and password
Find the user to fetch user info, groups, etc. For this step, you must specify a searchFilter that can find a user based on it's username, like "userPrincipalName={0}" where {0} is the provided username.
Define an administrative user who has the necessary permissions, and use that. You certainly shouldn't use the managerDN for anything in your application.

Spring Security Get Authentication on Session TimeOut : Authentication object is returning NULL on session timeout

I'm using spring/spring-security 3.1 and want to take some action depends on the authorities in ATHENTICATION object whenever session is timed out.AUTHENTICATION object is null on session timeout.
Here are the files for reference:
security config:
<logout invalidate-session="true" logout-url="/j_spring_security_logout" success-handler-ref="logoutSuccessHandler"/>
<!-- Custom filter to deny unwanted users even though registered -->
<custom-filter ref="blacklistFilter" before="FILTER_SECURITY_INTERCEPTOR" />
<!-- Custom filter for username, password and domain. The real customization is done in the customAuthenticationManager -->
<custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER" />
</http>
Note: tried with "invalidate-session="false"" as well.
and my custom LogoutSuccessHandler:
public class EnterpriseLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler{
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
for(GrantedAuthority authority: authentication.getAuthorities()){
if(authority.getAuthority()!=null)
logger.debug("THE CURRENT AUTHORITY FOR THE SESSION IS : {} ",authority.getAuthority().toString());
else
logger.debug("THE CURRENT AUTHORITY FOR THE SESSION IS authority.getAuthority(): NULL ");
if(authority.getAuthority()!=null && authority.getAuthority().equalsIgnoreCase(Operational.SPRING_SECURITY.LOGIN_SOURCE_BEACON)){
loginSource = authority.getAuthority().toString();
break;
}
}
}
}
So, there is the question how I can obtain Authentication object on the session timeout.
Can some help me to find out the solution for the above problem.
Thanks in advance.

Configuring spring security to redirect user to previous location after session timeout

I have a slight issue with my configuration of spring security and the InvalidSessionStrategy implementation I use.
Say a user is connected to the app and is viewing page: /userArea/thePage and their session times out, the user will first be redirected to the /signin page. Then, upon successful signin, they will be redirected to the home page of they personal area (/userArea) whereas I want them to come back to where they were located when the session timed out i.e. /userArea/thePage.
Is this possible?
If so how do I need to alter my config/app?
Here is my current config:
<beans:bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
<beans:constructor-arg name="securityContextRepository" ref="httpSessionSecurityContextRepository" />
<beans:property name="invalidSessionStrategy" ref="simpleRedirectInvalidSessionStrategy" />
</beans:bean>
<beans:bean id="simpleRedirectInvalidSessionStrategy" class="org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy">
<beans:constructor-arg name="invalidSessionUrl" value="/signin" />
<beans:property name="createNewSession" value="true" />
</beans:bean>
<http auto-config="true" use-expressions="true">
<custom-filter ref="sessionManagementFilter" before="SESSION_MANAGEMENT_FILTER" />
<form-login login-processing-url="/resources/j_spring_security_check" login-page="/signin" authentication-failure-url="/signin?login_error=t" default-target-url="/userArea" />
<logout logout-url="/resources/j_spring_security_logout" logout-success-url="/signin" />
...
edit 1: Let me better specify my requirements:
When a user session times out, I want the user to be redirected to the saved request (the url they requested before being redirected to the signin page).
However, when they initially signin with the app, I want them to be redirected to the home page of the personal area.
Are my requirements possible to implement using solution suggested by Carsten (see below)?
You could set the always-use-default-target="true" in the form-login tag. This redirects the user to the url they where trying to access before being intercepted to login.
But this will be the standard behaviour and not only in the case of a session timeout. Depending on the application this might not be what you want.
Edit:
To do what you want you need to find a way to save the information on which page the user was when the session timedout. I don't know of any out of the box solution for this problem, since there is no state that indicates whether or not the user timed out or logged out manually.
What needs to be done ist to:
set a flag or save the page-url on session timeout
check in a custom AuthenticationSuccesHandler and redirect accordingling
If I would implement somehing like that I would most likely store the page-url. Also there are a few tricky things with this from an UX perspective. What happens if the saved page relies on a state achieved earlier? (I assume thats the reason you want the User to go to the default-url on normal login?) What happens if the user just does not log out shuts down sleeps for the night and logs in navigating to the login page (does the flag/page-url time out?)? etc.
In general I think it would be better use the always-use-default-target="true" since this adds the comfort of bookmarking any page and not having to navigate there at each login.
Looks like it's common issue for any Spring project.
Spring developers thought that this is undocumented behavior https://github.com/spring-projects/spring-security/issues/1981, my business users are thinking that it's a bug.
So, as a result we need to do some custom implementation)))
Personally for me it's a bug and after making a custom implementation I don't understand why it's not fixed at Spring Framework.
As in a lot of other cases we have no choice and just copy-paste SimpleRedirectInvalidSessionStrategy and add our custom code.
You can even more simplify this code(I just make a customization which can be used OOTB in Spring):
public class CustomInvalidSessionStrategy implements InvalidSessionStrategy {
private final Log logger = LogFactory.getLog(this.getClass());
private String destinationUrl = null;
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private boolean createNewSession = true;
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException {
this.logger.debug("Starting new session (if required) and redirecting to '" + this.destinationUrl + "'");
if (this.createNewSession) {
request.getSession();
}
if (destinationUrl == null) {
this.redirectStrategy.sendRedirect(request, response, request.getRequestURI());
} else {
this.redirectStrategy.sendRedirect(request, response, this.destinationUrl);
}
}
public void setCreateNewSession(final boolean createNewSession) {
this.createNewSession = createNewSession;
}
public void setInvalidSessionUrl(final String invalidSessionUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), "url must start with '/' or with 'http(s)'");
this.destinationUrl = invalidSessionUrl;
}
}
And some extra configuration for Spring security:
<security:http ...>
...
<security:session-management invalid-session-strategy-ref="customInvalidSessionStrategy" />
...
</security:http>
<bean id="customInvalidSessionStrategy" class="com.custom.web.security.CustomInvalidSessionStrategy"/>

Resources