I am trying to implement a custom session timeout handler using spring security to add a dynamic parameter onto the redirect url and I have a problem where I am getting into an infinite loop but I don't know why. I was wondering if someone could enlighten me on this?
<http use-expressions="true" auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
<!-- custom filters -->
<custom-filter position="FORM_LOGIN_FILTER" ref="twoFactorAuthenticationFilter" />
<custom-filter after="SECURITY_CONTEXT_FILTER" ref="securityLoggingFilter"/>
<custom-filter before="SESSION_MANAGEMENT_FILTER" ref="sessionManagementFilter" />
<!-- session management -->
<session-management session-fixation-protection="none" />
<!-- error handlers -->
<access-denied-handler error-page="/accessDenied.htm"/>
<!-- logout -->
<logout
invalidate-session="false"
delete-cookies="JSESSIONID"
success-handler-ref="customUrlLogoutSuccessHandler"/>
<!-- authorize pages -->
<intercept-url pattern="/home.htm" access="isAuthenticated()" />
<intercept-url pattern="/shortsAndOvers.htm" access="isAuthenticated()" />
<intercept-url pattern="/shortsAndOversDaily.htm" access="isAuthenticated()" />
<intercept-url pattern="/birtpage.htm" access="isAuthenticated()" />
<intercept-url pattern="/reports/show.htm" access="isAuthenticated()" />
</http>
<beans:bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
<beans:constructor-arg name="securityContextRepository" ref="httpSessionSecurityContextRepository" />
<beans:property name="invalidSessionStrategy" ref="customSimpleRedirectInvalidSessionStrategy" />
</beans:bean>
<beans:bean id="customSimpleRedirectInvalidSessionStrategy" class="com.myer.reporting.security.CustomSimpleRedirectInvalidSessionStrategy">
<beans:constructor-arg name="invalidSessionUrl" value="/sessionExpired.htm" />
<beans:property name="createNewSession" value="false" />
</beans:bean>
<beans:bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/>
So far so good. When I get a session timeout the code in CustomSimpleRedirectInvalidSessionStrategy.onInvalidSessionDetected is called....
public CustomSimpleRedirectInvalidSessionStrategy(String invalidSessionUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), "url must start with '/' or with 'http(s)'");
this.destinationUrl = invalidSessionUrl;
}
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.debug("Starting new session (if required) and redirecting to '" + destinationUrl + "'");
SecurityContext securityContext = SecurityContextHolder.getContext();
String store = null;
ReportingManagerUser user = null;
if (securityContext != null){
Authentication authentication = securityContext.getAuthentication();
if (authentication!=null && !(authentication instanceof AnonymousAuthenticationToken)){
user = (ReportingManagerUser)authentication.getPrincipal();
if (user!=null){
store = user.getStore();
}
}
}
String amendedTargetUrl = null;
if (user !=null && user.isLoggedInWithSiteId()){
amendedTargetUrl =
destinationUrl.concat(
ParamConstants.PARAM_PREFIX +
ParamConstants.PARAM_SITE_ID +
ParamConstants.PARAM_EQ
+ store);
}else{
amendedTargetUrl = destinationUrl;
}
if (createNewSession) {
request.getSession();
}
redirectStrategy.sendRedirect(request, response, amendedTargetUrl);
}
And the code is executed properly but even after the redirect the code just keeps falling into the onInvalidSessionDetected method instead of actually redirecting to what I've configured in the invalidSessionUrl property.
I don't really get it.
thanks in advance
Silly mistake. I did not have security excluded on the redirect url. So it was trying to validation the jsession for that page and failing each time. Lol
Related
I've read and applied the Creating a Custom Login tutorial for custom login page and also custom authentication provider to my project.
As far as I understand from the documentations, spring security handles the login error by putting a parameter to the url such as "login?error=blabla".
But in my situation, no matter what user enters except for the true credentials, no request parameters are shown. But normal logins (meaning true logins) works fine.
Is there something I miss ?
CustomAuthenticationProviderImpl.java
#Component
public class CustomAuthenticationProviderImpl implements UnalAuthenticationProvider {
#Autowired
private CustomUserDetailService customUserDetailService;
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
UserDetails user = null;
try{
user = (UserDetails) customUserDetailService.loadUserByUsername(username);
}
catch(UsernameNotFoundException ex){
System.out.println("User name not found");
throw ex;
}
if (user == null) {
throw new BadCredentialsException("Username not found.");
}
if (!password.equals(user.getPassword())) {
throw new BadCredentialsException("Wrong password.");
}
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(user, password, authorities);
}
public boolean supports(Class<?> arg0) {
return true;
}
}
spring-security-config.xml
<global-method-security pre-post-annotations="enabled" />
<http auto-config="true">
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/access-denied" access="permitAll" />
<intercept-url pattern="/admin**" access="hasRole('ADMIN')" />
<intercept-url pattern="/**" access="hasRole('USER')" />
<form-login login-page="/login" default-target-url="/main" always-use-default-target="true"/>
<logout logout-url="/logout" logout-success-url="/"/>
<headers>
<frame-options policy="SAMEORIGIN"/>
</headers>
<session-management>
<concurrency-control expired-url="/login" />
</session-management>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customAuthenticationProviderImpl" />
</authentication-manager>
After giving some thought, I also decided to remove all intercept-url, thinking that might be a unintented redirection because of those... But it also didn't work.
At a first glance you miss a configuration parameter in your <form-login> section, namely the authentication-failure-url.
This is where you instruct Spring Security to redirect your failing login attempts.
Try to add it as follows:
<form-login login-page="/login" <!--other configurations--> authentication-failure-url="/login?error=true" />.
And, of course, take care of managing the error parameter in your custom login jsp.
I need a special redirect when session timeout expires. So I am trying to apply custom InvalidSessionStrategy, by using it in filter on SESSION_MANAGEMENT_FILTER level.
I have the following security configuration
<http auto-config="false" entry-point-ref="customAuthenticationEntryPoint" create-session="ifRequired" >
<custom-filter before="SESSION_MANAGEMENT_FILTER" ref="customSessionManagementFilter" />
<custom-filter before="BASIC_AUTH_FILTER" ref="customAuthFilter" />
<intercept-url pattern="/userpage*" access="hasRole('USER')" />
<intercept-url pattern="/greetpage*" access="hasAnyRole('USER')" />
<logout logout-url="/logout" success-handler-ref="customLogoutSuccessHandler" />
<headers>
<frame-options policy="SAMEORIGIN" />
</headers>
<csrf disabled="true" />
</http>
<beans:bean id="customSessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
<beans:constructor-arg name="securityContextRepository" ref="httpSessionSecurityContextRepository" />
<beans:property name="invalidSessionStrategy" ref="customInvalidSessionStrategy" />
</beans:bean>
<beans:bean id="customInvalidSessionStrategy" class="com.test.CustomInvalidSessionStrategy">
<beans:constructor-arg name="customHandlerService" ref="customHandlerService"/>
</beans:bean>
<beans:bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/>
<beans:bean id="customHandlerService" class="com.test.CustomHandlerService" />
and CustomInvalidSessionStrategy
public class CustomInvalidSessionStrategy implements InvalidSessionStrategy {
private CustomHandlerService customHandlerService;
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Autowired
public CustomInvalidSessionStrategy(CustomHandlerService customHandlerService) {
this.customHandlerService = customHandlerService;
}
#Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String requestUrl = getRequestUrl(request);
if(requestUrl.contains("/api")) {
String refererUrl = request.getHeader("referer");
String redirectUrl = customHandlerService.getRedirectLogoutUrl(refererUrl);
redirectStrategy.sendRedirect(request, response, redirectUrl);
} else {
request.getSession(true);
redirectStrategy.sendRedirect(request, response, requestUrl);
}
}
private String getRequestUrl(HttpServletRequest request) {
StringBuffer requestURL = request.getRequestURL();
String queryString = request.getQueryString();
if (StringUtils.hasText(queryString)) {
requestURL.append("?").append(queryString);
}
return requestURL.toString();
}
}
The problem is that my CustomInvalidSessionStrategy is never called.
From breakpoint in SessionManagementFilter.doFilter() I see that it's invalidSessionStrategy is null, although
SessionManagementFilter.setInvalidSessionStrategy() DID set my custom filter into it.
Will appreciate any advice,
Thanks
The question is invalid. the problem was that ajax sent requests to other url, shich wasn't configured to use custom message filter. that is why invalid session strategy was null
Currently my spring-security.xml looks like this:
<global-method-security pre-post-annotations="enabled" />
<http pattern="/login" security="none"/>
<http pattern="/assets/**" security="none"/>
<http auto-config="false" entry-point-ref="authenticationEntryPoint" disable-url-rewriting="true">
<intercept-url pattern="/**" access="ROLE_USER"/>
<intercept-url pattern="/admin/**" access="ROLE_ADMIN"/>
<intercept-url pattern="/tadmin/**" access="ROLE_TENANT_ADMIN"/>
<form-login login-page="/login" authentication-success-handler-ref="authenticationSuccessHandler" authentication-failure-url="/login?error"/>
<logout logout-url="/logout" logout-success-url="/login"/>
<remember-me/>
</http>
<beans:bean id="authenticationSuccessHandler" class="com.dj.LoginSuccessHandler">
<beans:property name="useReferer" value="true"/>
</beans:bean>
<beans:bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/login" />
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<!-- <password-encoder hash="md5"/> -->
<user-service>
<user name="user" password="123" authorities="ROLE_USER"/>
<user name="admin" password="123" authorities="ROLE_ADMIN,ROLE_USER"/>
<user name="tadmin" password="123" authorities="ROLE_TENANT_ADMIN,ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
My custom AuthenticationSuccessHandler:
package com.dj;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import com.dj.UserRole;
public class LoginSuccessHandler extends
SavedRequestAwareAuthenticationSuccessHandler {
// getters and setters for injected services
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) {
try {
String redirectUrl = "/login";
if (hasRole(authentication, UserRole.ROLE_ADMIN)) {
redirectUrl = "/app/admin/secure";
} else if (hasRole(authentication, UserRole.ROLE_TENANT_ADMIN)) {
redirectUrl = "/app/tadmin/secure";
} else if (hasRole(authentication, UserRole.ROLE_USER)) {
redirectUrl = "/app/USER/";
}
response.sendRedirect(redirectUrl);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Check if a role is present in the authorities of current user
*
* #param authorities
* all authorities assigned to current user
* #param role
* required authority
* #return true if role is present in list of authorities assigned to
* current user, false otherwise
*/
private boolean hasRole(Authentication auth, UserRole role) {
boolean hasRole = false;
for (GrantedAuthority grantedAuthority : auth.getAuthorities()) {
hasRole = grantedAuthority.getAuthority().equals(role.name());
if (hasRole)
break;
}
return hasRole;
}
}
When I try to log in i can see by intercepting network traffic:
A POST from my custom login form sending username,password,remember me to j_spring_security_check
A GET from the app/admin/secure page
However, I am never getting redirected to the correct page given the type of user that has just been logged in, stuck forever on the login page.
When entering the redirect url manually everything works fine and I am logged in properly.
Seems to me security is set up properly, however the redirecting is just not working.
Any help on this matter would be appreciated.
Your intercept-url declarations are in the wrong order. You need to put the most-specific ones first. You have /** at the top, so that will always match. It should be the last in the list.
You should be able to track the successful login and a subsequent access-denied exception in the debug log.
I am using Spring and Ldap for user authentication , Now I want to redirect the user to homepage when user is already logged in , I tried few solutions that I read through google but no luck. Here is my configuration code ...
Spring-Security.xml
<security:http auto-config="true" use-expressions="true"
access-denied-page="/denied" access-decision-manager-ref="accessDecisionManager"
disable-url-rewriting="true">
<security:remember-me key="_spring_security_remember_me"
token-validity-seconds="864000" token-repository-ref="tokenRepository" />
<security:intercept-url pattern="/login/login"
access="permitAll" />
<security:intercept-url pattern="/resources/**"
access="permitAll" />
<security:intercept-url pattern="/member/*"
access="permitAll" />
<security:intercept-url pattern="user/admin/admin"
access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/user/user"
access="hasRole('ROLE_USERS')" />
<security:form-login login-page="/login/login"
authentication-failure-url="/login/login?error=true"
default-target-url="/checking" />
<security:logout invalidate-session="true"
logout-success-url="/login/login" logout-url="/login/logout" />
<security:custom-filter ref="captchaCaptureFilter"
before="FORM_LOGIN_FILTER" />
<!-- <security:custom-filter ref="captchaVerifierFilter" after="FORM_LOGIN_FILTER"
/> -->
<!-- <security:session-management
invalid-session-url="/logout.html">
<security:concurrency-control
max-sessions="1" error-if-maximum-exceeded="true" />
</security:session-management>
<security:session-management
session-authentication-strategy-ref="sas" /> -->
</security:http>
My Login Controller
public class LoginController {
#Autowired
private User user;
#Autowired
SecurityContextAccessor securityContextAccessor;
#RequestMapping(value = "login/login", method = RequestMethod.GET)
public String getLogindata(
#RequestParam(value = "error", required = false) String error,
Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
model.addAttribute("error", error);
if (securityContextAccessor.isCurrentAuthenticationAnonymous() && auth.getPrincipal() == null) {
return "login/login";
} else {
return "redirect:/member/user";
}
}
}
auth.getName() always gives me anonymous user even if I am already logged in....
You should write your target redirection to the home page,
after successfully login in to the default-target-url in spring-security.xml, instead of default-target-url="/checking" .
I am not sure what are you trying to achieve in /login/login Controller ?
If you only want to redirect user, after successfully login to the /member/user , you should write:
<security:form-login login-page="/login"
always-use-default-target='true'
default-target-url="/member/user"
authentication-failure-url="/login/login?error=true"
/>
And login Controller should look like this:
#RequestMapping(value="/login", method = RequestMethod.GET)
public String login(ModelMap model) {
return "login";
}
If your authentication provider is OK and correct , Spring security will redirect you automatically to the /member/user after successfully login.
I have this Spring XML:
<!-- Configure the authentication -->
<security:http auto-config="true" use-expressions="true">
<security:form-login login-page="/login"
authentication-failure-url="/login?error=true"
default-target-url="/index" />
<security:logout invalidate-session="true"
logout-success-url="/login"
logout-url="/logout" />
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="testUDS" />
</security:authentication-manager>
<bean id="testUDS" class="net.safecycle.services.security.TestUserDetailService" />
My UserDetailsService implementation looks like this:
public class TestUserDetailService implements UserDetailsService {
public UserDetails loadUserByUsername
(
String username
) throws UsernameNotFoundException {
System.out.println ("loadUserByUsername (" + username + ")");
Collection<GrantedAuthority> authorities;
authorities = new LinkedList<GrantedAuthority> ();
authorities.add (new GrantedAuthorityImpl ("Admin"));
UserDetails ud = new User (username,
"ca",
true,
true,
true,
true,
authorities);
return ud;
}
}
When I log in with any username and the password 'ca', I should see the print statement at the top of my loadUserByUsername, but I do not. What is most perplexing is that I have used this code in another project with no problem. Is there anything I am missing, a copy mistake I'm hoping?
Here is my code from my xml file. Only missing thing is the allias.. try to add alias="authenticationManager" maybe it will help.
<beans:bean id="CustomUserDetailsService"
class="com.dennis.ehospital.hibernate.security.CustomUserDetailsService" />
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="CustomUserDetailsService" />
</authentication-manager>
Try to specify which resources are protected
<security:intercept-url pattern="/**" access="isAuthenticated()"/>