I'm running in my web app filter, which recieves from external source info about user, if he's logged in or not. Heres my filter:
#Override
public void doFilter( ServletRequest request, ServletResponse response,
FilterChain chain ) throws IOException, ServletException
{
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String loginBean = httpRequest.getHeader( CommonVariables.LOGIN_BEAN );
if ( loginBean == null )
{
System.out.println( "FILTER-----------" );
try
{
String login;
String domain;
//Here i'm getting login and domain string
loginBean = domain + "\\" + login;
httpResponse.addHeader( "LoginBean", loginBean );
System.out.println( login + " " + domain );
} catch ( Exception e )
{
e.printStackTrace();
//redirect to login page
httpResponse.sendRedirect( "..." );
return;
}
}
chain.doFilter( request, response );
}
Not I though that those header will be passed into next filters. Therefore I implemented Spring Security PRE_AUTH_FILTER:
Spring security context
<http use-expressions="true" auto-config="false" entry-point-ref="http403EntryPoint">
<!-- Additional http configuration omitted -->
<custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</http>
<beans:bean id="siteminderFilter" class=
"org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<beans:property name="principalRequestHeader" value="LoginBean"/>
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="preauthAuthProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService">
<beans:bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:property name="userDetailsService" ref="userDetailsService"/>
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="http403EntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
<beans:bean id="userDetailsService" class="com.execon.security.CustomUserDetailsService"/>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="preauthAuthProvider" />
</authentication-manager>
Then I tried to parse loginBean String in my CustoUserDetailsService and receive actuall user object. But It is not fired, and app fails with this:
org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException: LoginBean header not found in request.
So that means the header is set wrong? Or not set at all? What might be wrong?
Filter setting LoginBean is first one fired, then goes Spring SEcurity. Standard output works ok as I have:
17:12:15,669 INFO [stdout] (http--127.0.0.1-8080-2) FILTER-----------
17:12:15,669 INFO [stdout] (http--127.0.0.1-8080-2) LOGIN DOMAIN
You are setting something in the response and the Spring's class is looking for the same in the request.
The only way you can modify an incoming HttpServletRequest is to decorate it. You should define a class as follows first:
public class AuthHttpServletRequest extends HttpServletRequestWrapper
{
private String loginBean;
public AuthHttpServletRequest(HttpServletRequest aRequest, String loginBean)
{
super(aRequest);
this.loginBean = loginBean;
}
#Override
public String getHeader(String headerName)
{
if(CommonVariables.LOGIN_BEAN.equals(headerName)) {
return this.loginBean;
}
return super.getHeader(headerName);
}
}
Then, replace the following line in your filter:
httpResponse.addHeader( "LoginBean", loginBean );
with this:
request = new AuthHttpServletequest(httpRequest, loginBean);
Then your chain.doFilter gets the request that can return the loginBean as you intended it to, to the Spring's authentication filter class, down in the filter chain.
Related
I use Spring i18n library.
- ReloadableResourceBundleMessageSource
- SessionLocaleResolver
- LocaleChangeInterceptor
There are no errors.
If I use a strong refresh function in chrome, all interceptor methods are executed twice more.
And The Issue only occurs when the pages is used messages.properties.
preHandle ==> Controller ==> postHandle ==> AfterCompletion is executed, and all methods are executed twice more.
I've looked at the lifecycle of the interceptor, but I don't understand why the interceptor runs twice more without reason.
1. servlet-context.xml
<beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basenames" value="/WEB-INF/messages/messages, /WEB-INF/messages/messages_ko_KR" />
</beans:bean>
<beans:bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<beans:property name="defaultLocale" value="ko_KR" />
</beans:bean>
<interceptors>
<beans:bean id="localeChangeInterceptor" class="com.test.util.locale.LocaleChangeInterceptor" />
</interceptors>
2. LocaleChangeInterceptor is implemented by me and is implemented as simple as the code below.
public class LocaleChangeInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String language = request.getParameter("language");
System.out.println("1. preHandle(): " + language);
if(language != null) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
localeResolver.setLocale(request, response, StringUtils.parseLocaleString(language));
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("2. postHandle()");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("3. afterCompletion()");
}
}
Console
1. preHandle(): null
main.jsp
2. postHandle()
3. afterCompletion()
1. preHandle(): null
2. postHandle()
3. afterCompletion()
1. preHandle(): null
2. postHandle()
3. afterCompletion()
I don't know whether it is a problem or a natural result of Interceptor's lifecycle.
Resolved.
It was resolved by first creating an Interceptor bean,
then by calling the registered bean by reference from the .
It seems to be an issue because I didn't know well about Spring interceptor registration. I need to find out more.
<beans:bean id="localeChangeInterceptor" class="com.test.util.locale.LocaleChangeInterceptor" />
<interceptors>
<interceptor>
<mapping path="/**" />
<exclude-mapping path="/resources/**" />
<beans:ref bean="localeChangeInterceptor" />
</interceptor>
</interceptors>
I am working on a Spring-MVC application in which we are using Spring-Security for authentication and authorization. In our application we send out emails with URL's. Many times the user is not logged in, but after login, we would want to redirect to the original link after login. Thank you. Tried XML configuration, but it is not working.
Xml config :
<!-- Global Security settings -->
<security:http pattern="/resources/**" security="none"/>
<security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true">
<security:form-login login-page="/login" username-parameter="j_username" password-parameter="j_password"
login-processing-url="/j_spring_security_check" default-target-url="/canvaslisting"
always-use-default-target="true" authentication-failure-url="/denied"/>
<security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService"
token-validity-seconds="1209600" data-source-ref="dataSource"/>
<security:logout delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout"/>
<security:intercept-url pattern="/**" requires-channel="https"/>
<security:port-mappings>
<security:port-mapping http="8080" https="8443"/>
</security:port-mappings>
<security:logout logout-url="/logout" logout-success-url="/" success-handler-ref="myLogoutHandler"/>
<security:session-management session-fixation-protection="newSession">
<security:concurrency-control session-registry-ref="sessionReg" max-sessions="5" expired-url="/login"/>
</security:session-management>
</security:http>
<beans:bean id="sessionReg" class="org.springframework.security.core.session.SessionRegistryImpl"/>
<beans:bean id="rememberMeAuthenticationProvider"
class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:constructor-arg index="0" value="_spring_security_remember_me"/>
<beans:constructor-arg index="1" ref="userDetailsService"/>
<beans:constructor-arg index="2" ref="jdbcTokenRepository"/>
<property name="alwaysRemember" value="true"/>
</beans:bean>
<beans:bean id="jdbcTokenRepository"
class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<beans:property name="createTableOnStartup" value="false"/>
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<!-- Remember me ends here -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="LoginServiceImpl">
<security:password-encoder ref="encoder"/>
</security:authentication-provider>
</security:authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11"/>
</beans:bean>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
<beans:bean id="authenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="filterProcessesUrl" value="/login" />
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationSuccessHandler">
<beans:bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="useReferer" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="authenticationFailureHandler">
<beans:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login?login_error=t" />
</beans:bean>
</beans:property>
</beans:bean>
</beans>
Thank you.
Although you have not shown how you have configured your tag, so I am assuming that, you already have, that see bellow code.
<security:http auto-config="true" use-expressions="false">
<security:form-login login-page="/login" login-processing-url="/login"
username-parameter="custom_username"
password-parameter="custom_password"
default-target-url="/appointments/"
always-use-default-target="true"
authentication-failure-url="/login?error=true"
/>
<security:intercept-url pattern="/appointments/*" access="ROLE_USER"/>
<security:intercept-url pattern="/schedule/*" access="ROLE_FOO"/>
<security:intercept-url pattern="/**" access="ROLE_ANONYMOUS, ROLE_USER"/>
</security:http>
so, if you are not mentioning these two lines
default-target-url="/appointments/"
always-use-default-target="true"
, then by default spring security will redirect you user to the same page (which he requested), and got authentication prompt.
If this still does not full fills your requirement then you may need to implement your own filter or take a look at FilterSecurityInterceptor or MethodSecurityInterceptor .
Bellow is an Example, which will help you to achieve your goal:
To understand this problem better, take a look on below example:
- user receives a newsletter mail where he's invited to vote for more beautiful holidays picture. The URL contains a hash parameter used to authenticate user in the page.
- when user clicks on given URL, it will arrive to voting page as already authenticated user.
So My Custom Filter Will be like bellow:
package com.osigu.ehr.config;
public class OneShotActionFilter extends GenericFilterBean {
private static final Logger LOGGER =
LoggerFactory.getLogger(OneShotActionFilter.class);
private static Map<String, String> users = new HashMap<String, String>();
static {
users.put("0000000000001", "bartosz");
users.put("0000000000002", "admin");
users.put("0000000000003", "mod");
}
private static final String PARAM_NAME = "uio";
private AuthenticationManager authenticationManager;
private UserDetailsService userDetailsService;
private final RedirectStrategy redirectStrategy = new
DefaultRedirectStrategy();
private enum AuthenticationStates {
REDIRECT, CONTINUE;
}
public void setAuthenticationManager(AuthenticationManager
authenticationManager) {
this.authenticationManager = authenticationManager;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException,
ServletException {
LOGGER.debug("One shot filter invoked");
if (attemptAuthentication(request) == AuthenticationStates.REDIRECT) {
// Note that we should handle that dynamically but for learning purposes
//we'll consider that one-shot
// authentication works only for this URL
this.redirectStrategy.sendRedirect((HttpServletRequest) request,
(HttpServletResponse) response,
"/secret/one-shot-action");
} else {
LOGGER.debug("User was not correctly authenticated, continue filter chain");
// continue execution of all other filters
// You can test the code without this fragment in the pages without ?uio parameter. You should see blank page because of
// security filter chain interruption.
filterChain.doFilter(request, response);
}
}
private AuthenticationStates attemptAuthentication(ServletRequest request) {
AuthenticationStates state = AuthenticationStates.CONTINUE;
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
String code = request.getParameter(PARAM_NAME);
if ((authentication == null || !authentication.isAuthenticated()) && code !=
null &&
users.containsKey(code)) {
LOGGER.debug("Checking user for code " + code);
UserDetails user = userDetailsService.loadUserByUsername(users.get(code));
LOGGER.debug("Found user from code (" + users.get(code) + "). User found is " + user);
if (user != null) {
users.remove(code);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user.getUsername(),
user.getPassword());
authentication = this.authenticationManager.authenticate(authRequest);
if (authentication != null && authentication.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authentication);
state = AuthenticationStates.REDIRECT;
}
}
}
return state;
}
}
And Finally Configure it in your xml, like bellow
<security:http authentication-manager-ref="frontend" auto-config="true" use-
expressions="true" access-denied-page="/access-denied">
<-- other filters are defined here -->
<security:custom-filter ref="oneShootAuthFilter"
after="CONCURRENT_SESSION_FILTER"/>
</security:http>
<bean id="oneShootAuthFilter"
class="com.waitingforcode.security.filter.OneShotActionFilter">
<property name="authenticationManager" ref="frontend" />
<property name="userDetailsService" ref="inMemoryUserService" />
</bean>
To test the filter, we can try to access to http://localhost:8080/?uio=0000000000001. You should be redirected (but only once) to http://localhost:8080/secret/one-shot-action page
I wrote this example for better clarity and as reference for future questions.
Do up vote if you think it helped you to clear your doubt.
you can also visit this link
I am working on a Java application which connects to a Spring-MVC server, using Spring-Security for authentication/authorization. The login part works, and I get a JSESSIONID back in the Java application, but when I make request to secured resource, it fails, Spring-Security is unable to find any logged in user. What am I doing wrong here?
security-applicationContext.xml :
<security:http pattern="/resources/**" security="none"/>
<security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true">
<security:form-login login-page="/login" login-processing-url="/j_spring_security_check"
default-target-url="/dashboard" always-use-default-target="false"
authentication-failure-url="/denied"/>
<security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService"
token-validity-seconds="1209600" data-source-ref="dataSource"/>
<security:logout delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout"/>
<!--<security:intercept-url pattern="/**" requires-channel="https"/>-->
<security:port-mappings>
<security:port-mapping http="8080" https="8443"/>
</security:port-mappings>
<security:logout logout-url="/logout" logout-success-url="/" success-handler-ref="myLogoutHandler"/>
<security:session-management session-fixation-protection="migrateSession">
<security:concurrency-control session-registry-ref="sessionRegistry" max-sessions="5" expired-url="/login"/>
</security:session-management>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="restaurantauthenticationprovider"/>
<security:authentication-provider ref="userauthenticationprovider"/>
</security:authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11"/>
</beans:bean>
<beans:bean id="restaurantauthenticationprovider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
<beans:bean id="userauthenticationprovider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="UserLoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
As I have 2 tables to check from which to login, I have 2 DAOAuthenticationProviders.
UserLoginServiceImpl :
#Transactional
#Service("loginuserDetailsService")
public class UserLoginServiceImpl implements UserDetailsService {
#Autowired
private PersonDAO personDAO;
#Autowired
private UserAssembler userAssembler;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,DataAccessException {
System.out.println("Username is "+username);
Person person = this.personDAO.findPersonByUserName(username.toLowerCase());
if(person == null) { throw new UsernameNotFoundException("Wrong username or password");}
return userAssembler.buildUserFromUserEntity(person);
}
}
Assembler :
#Service("userassembler")
#Transactional
public class UserAssembler {
#Transactional
User buildUserFromUserEntity(Person userEntity){
System.out.println("We are in Userassembler"+userEntity.getEmail());
String username = userEntity.getUsername().toLowerCase();
String password = userEntity.getPassword();
boolean enabled = userEntity.isEnabled();
boolean accountNonExpired = userEntity.isAccountNonExpired();
boolean credentialsNonExpired = userEntity.isCredentialsNonExpired();
boolean accountNonLocked = userEntity.isAccountNonLocked();
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new User(username,password,enabled,accountNonExpired,credentialsNonExpired,accountNonLocked,authorities);
}
}
The above is the config, now I will put the rest code which is failing :
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Log.d("Username is ", username);
String jsessionid = rest.execute("http://192.168.178.60:8080/j_spring_security_check", HttpMethod.POST,
new RequestCallback() {
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
request.getBody().write(("j_username=" + username + "&j_password=" + password).getBytes());
}
}, new ResponseExtractor<String>() {
#Override
public String extractData(ClientHttpResponse response) throws IOException {
List<String> cookies = response.getHeaders().get("Cookie");
if (cookies == null) {
cookies = response.getHeaders().get("Set-Cookie");
}
String cookie = cookies.get(cookies.size() - 1);
System.out.println("Cookie is " + cookie);
// The method below gets me which user is logged in, and I always get null for Controller method.
reply = rest.getForObject(
"http://192.168.178.60:8080/dashboard", String.class);
int start = cookie.indexOf('=');
int end = cookie.indexOf(';');
return cookie.substring(start + 1, end);
}
});
}
});
thread.start();
Update
Finally, the code which worked :
// I am getting the cookie from the server, which I am setting manually for every request, cookie is a static volatile string.
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", "JSESSIONID=" + StaticRestTemplate.jsessionid);
HttpEntity requestEntity = new HttpEntity(null, requestHeaders);
ResponseEntity rssResponse = rest.exchange(
"http://192.168.178.60:8080/dashboard",
HttpMethod.GET,
requestEntity,
String.class);
String abc = (String) rssResponse.getBody();
Spring's RestTemplate does not keep track of cookies by default. This ensures you don't accidentally pass a cookie (i.e. JSESSIONID) from one user on behalf of another user (i.e. think of using the RestTemplate on a server where many users are leveraging the same RestTemplate).
If you want to do this you can configure it using something like this:
RestTemplate rest = new RestTemplate();
// initialize the RequestFactory to allow cookies
HttpClient httpClient = HttpClientBuilder.create().build();
ClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
rest.setRequestFactory(factory);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("username", "user");
map.add("password", "password");
String result = rest.postForObject("http://localhost:8080/login", map, String.class);
String hello = rest.postForObject("http://localhost:8080/", map, String.class);
assertThat(hello).isEqualTo("Hello");
To use this code you will need to ensure you have httpclient on your classpath. For example, the following might be in your pom.xml if you are using Maven:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
Obviously you will need to ensure you include the version of httpclient that works for your dependencies.
I'm working with Spring Security 3.2 and Hibernate 4. Currently I have a custom login wich works as follows . The URL "/" (root ) is a welcome jsp requests wich ask for a parameter to display a different login according to the same parameter . For example if the user enters the url "/parameter1" (manual action ) , this variable shows me a personalized login generated by a driver that cathes a RequestMapping ( value = " /{parameter}"from there, all URLS will have that parameter , the problem that I have is that when the user wishes to leave or your session expires , spring sends me the url "/" , but I need it to send me a /parameter1 , in order to capture the parameter "parameter1" so that It leaves me in the custom login. That way I would not have to manually re- enter the parameter . My security settings are as follows:
<custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
<!-- <form-login login-page="/loginUser" login-processing-url="/testUser/j_spring_security_check"
authentication-failure-url="/loginError" default-target-url="/testUser"
username-parameter="j_username" password-parameter="j_password" /> -->
<logout invalidate-session="true" delete-cookies="JSESSIONID" logout-success-url="/loginUser" logout-url="/testUser/j_spring_security_logout"/>
<session-management invalid-session-url="/" session-fixation-protection="migrateSession" >
<concurrency-control max-sessions="2" expired-url="/" error-if-maximum-exceeded="false"/>
</session-management>
<beans:bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/loginUser" />
</beans:bean>
<beans:bean id="myFilter" class="net.universia.test.autenticacionService.LoginAuthenticationFilter">
<beans:property name="authenticationManager" ref='UserauthenticationManager'/>
<beans:property name="authenticationFailureHandler" ref="failureHandler"/>
<beans:property name="authenticationSuccessHandler" ref="successHandler"/>
<beans:property name="filterProcessesUrl" value="/testUser/j_spring_security_check"/>
</beans:bean>
<beans:bean id = "exceptionTranslationFilter" class = "org.springframework.security.web.access.ExceptionTranslationFilter" >
<beans:property name = "authenticationEntryPoint" ref = "loginUrlAuthenticationEntryPoint" />
<beans:property name = "accessDeniedHandler" ref = "accessDeniedHandler" />
</beans:bean>
<beans:bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/testUser"/>
</beans:bean>
<beans:bean id = "accessDeniedHandler" class = "org.springframework.security.web.access.AccessDeniedHandlerImpl" >
<beans:property name = "errorPage" value = "/403" />
</beans:bean>
And the driver that displays the login form is:
#RequestMapping(value ="/{testRef}", method = {RequestMethod.POST,RequestMethod.GET})
public #ResponseBody ModelAndView loginTestRef(#PathVariable("testRef") String testRef,HttpSession session, HttpServletRequest request) {
session.setAttribute("ssidreffh", testRef);
TestDatos test = testService.showTestUserByRef(testRef);
request.getSession().setAttribute("test", test);
ModelAndView mav = new ModelAndView("/loginUser");
mav.addObject("test", test);
return mav;
}
If the user is in the url /dominio/parametro1/paginaPerfil goes or your session ends, spring redirect me to the url "/myApp/parameter1"
and so would be in the login and not the root "/".
I could finally resolve my problem. I implemented a custom filter for logging out with SimpleUrlLogoutSuccessHandler and I could capture the previous URL and from that the parameter that I return with a redirect (/parameter1). This is my code:
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
#Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String testRef = null;
if (authentication != null) {
String refererUrl = request.getHeader("Referer");
System.out.println("variables: " +refererUrl);
String[] parts = refererUrl.split("/");
testRef = parts[5];
}
setDefaultTargetUrl("/"+testRef);
super.onLogoutSuccess(request, response, authentication);
}
}
How can I add my own logout handler to LogoutFilter in spring-security ?
Thanks!
The following solution works for me and may be helpful:
Extend the SimpleUrlLogoutSuccessHandler or implement the LogoutHandler:
public class LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
// Just for setting the default target URL
public LogoutSuccessHandler(String defaultTargetURL) {
this.setDefaultTargetUrl(defaultTargetURL);
}
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// do whatever you want
super.onLogoutSuccess(request, response, authentication);
}
}
Add to your Spring Security Configuration:
<security:logout logout-url="/logout" success-handler-ref="logoutSuccessHandler" />
<bean id="logoutSuccessHandler" class="your.package.name.LogoutSuccessHandler" >
<constructor-arg value="/putInYourDefaultTargetURLhere" />
</bean>
See the answer in this post in the Spring Security Forum:
XML Definition:
<beans:bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
<custom-filter position="LOGOUT_FILTER"/>
<beans:constructor-arg index="0" value="/logout.jsp"/>
<beans:constructor-arg index="1">
<beans:list>
<beans:ref bean="securityContextLogoutHandler"/>
<beans:ref bean="myLogoutHandler"/>
</beans:list>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="securityContextLogoutHandler" class="org.springframework.security.ui.logout.SecurityContextLogoutHandler"/>
<beans:bean id="myLogoutHandler" class="com.whatever.CustomLogoutHandler">
<beans:property name="userCache" ref="userCache"/>
</beans:bean>
LogoutHandler class:
public class CustomLogoutHandler implements LogoutHandler {
private UserCache userCache;
public void logout(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) {
// ....
}
#Required
public void setUserCache(final UserCache userCache) {
this.userCache = userCache;
}
}
You can use java-config solutions like this.
#Configuration
#EnableWebSecurity
public class SpringSecurity2Config extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//you can set other security config by call http.XXX()
http
.logout()
.addLogoutHandler(new CustomLogoutHandler())
.logoutUrl("/logout")
.logoutSuccessHandler(...)
.permitAll();
}
static class CustomLogoutHandler implements LogoutHandler {
#Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
//...
}
}
}
You should use success-handler-ref attribute of <logout> element:
<security:logout invalidate-session="true"
success-handler-ref="myLogoutHandler"
logout-url="/logout" />
As alternative solution you can configure your own filter on the logout URL.