Issue
I recently switched to Spring Security to handle login, logout and role based acces for my Web application. I use flash messages for info and error messages and want to show such a message after succesfull login and logout.
In the JSP files, those messages are shown using following code:
<c:if test = "${not empty flashmessage_error}" >
<div class="alert alert-danger"><c:out value="${flashmessage_error}" />
</div>
</c:if>
<c:if test="${not empty flashmessage_info}" >
<div class="alert alert-info"><c:out value="${flashmessage_info}" />
</div>
</c:if>
To show a flash message after succesful login, I defined a custom LoginSuccessHandler which puts the message in a FlashMap and sets the redirect page to the home page:
public class FlashMessageAuthSuccessHandler extends
SavedRequestAwareAuthenticationSuccessHandler {
private String flashMessage = "Logged in!";
public void setFlashMessage(String flashMessage) {
this.flashMessage = flashMessage;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
FlashMapManager flashMapManager = new SessionFlashMapManager();
FlashMap fm = new FlashMap();
fm.put(KeyConstants.FLASH_INFO_KEY, flashMessage);
flashMapManager.saveOutputFlashMap(fm, request, response);
setDefaultTargetUrl("/");
super.onAuthenticationSuccess(request, response, authentication);
}
}
My SecurityConfig class looks as follows:
@Configuration
@EnableWebSecurity(debug = false)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private MessageSource messageSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
/*
* HttpSecurity allows configuring web based security for specific HTTP requests
* By default it will be applies to all request, but can be restricted
* using requestMatcher
* @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//super.configure(http);
http
.authorizeRequests()
.antMatchers("/", "/css/**", "/js/**", "/images/**", "/about", "/error").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.usernameParameter("j_username")
.passwordParameter("j_password")
.loginPage("/login")
.loginProcessingUrl("/ssprocesslogin") //post request
// .defaultSuccessUrl("/") //cannot possibly used in combination with successHandler?
.successHandler(loginSuccesHandler())
.failureUrl("/login?" + KeyConstants.FLASH_ERROR_KEY + "=Verkeerde login message aanpassen") //default is /login?error
.permitAll()
.and()
.rememberMe()
.tokenValiditySeconds(2592000) //one month
.rememberMeParameter("j_rememberme")
.key("wIllekEUrigeSleutteL")
.userDetailsService(userDetailsService)
.and()
.logout()
.logoutUrl("/logout") //default is /logout
// .logoutSuccessUrl("/") //default is /login?logout
.logoutSuccessHandler(new FlashMessageLogoutSuccessHandler())
.invalidateHttpSession(true) //true is the default
;
}
@Bean
public FlashMessageAuthSuccessHandler loginSuccesHandler() {
FlashMessageAuthSuccessHandler handler = new FlashMessageAuthSuccessHandler();
//THIS DOES NOT WORK!!!!!
String loginSuccessMessage = messageSource.getMessage("flashmessage.loginsuccesful", null, "not found", null);
handler.setFlashMessage(loginSuccessMessage);
return handler;
}
@Bean
public AuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider dap = new DaoAuthenticationProvider();
dap.setUserDetailsService(userDetailsService);
dap.setPasswordEncoder(passwordEncoder());
return dap;
}
@Bean
public ShaPasswordEncoder passwordEncoder() {
return new ShaPasswordEncoder(256);
}
}
But the proper message from the messages.properties file is not shown. Instead the string "not found" is shown as the flash message after log in. In the line below, I try to use the injected MessageSource to look up the message by the message key but obviously it is not found and the fallback message is shown:
String loginSuccessMessage = messageSource.getMessage("flashmessage.loginsuccesful", null, "not found", null);
The messageSource bean is defined in the MvcConfig file as shown below and works fine when used to show messages in the JSP files but it seems it cannot be autowired in the Spring Security config file to obtain tyhe desired flash messages.
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
Any ideas how I can accomplish this?
Solution
I think this is indeed the problem that the MessageSource is not ready to inject in the Configuration object. Your solutions will probably work and involves reading a property from the application.properties file using a @Value annotation. I do already read this file in my AppConfig class:
@Configuration
@PropertySource(value= {"classpath:application.properties"})
public class AppConfig {
}
and access the desired value in one of the other configuration files injecting the Environment object:
@Autowired
private Environment env;
and in the loginSuccesHandler() method:
String loginSuccessMessage = env.getProperty("flashmessage.loginsuccesful");
This does work and partly solves the issue. The remaining issue is that the property declaration:
flashmessage.loginsuccesful=You have been logged in succesfully!
needed to be moved from messages.properties to application.properties while my primary reason to use a property was to define it together with the other localized messages, probably in different languages in the future. But maybe it is simply not possible because the locale is not known at this point?
UPDATE: I solved the issue by moving the declaration of the MessageSource bean from the Dispatcher servlet context (MvcConfig.java) to the Root context (AppConfig.java). The SecurityConfig class obviously has no access to the servlet context.
Answered By - klausch
Answer Checked By - David Marino (JavaFixing Volunteer)