Spring setLocale - spring

I have a custom public Locale resolveLocale(HttpServletRequest request) { in place that checks for browser language but also checks if the user is authenticated, because if so it will change the locale again based on the settings of the user.
Now I'm trying to add a language selector to an unauthenticated page, by allowing pages to hold the lang parameter as explained in the docs. I currently have this:
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
I've noticed, by going to the page with for example ?lang=en added, that the setLocale is being called and here's where I'm stuck. I cannot seem to find out how to actually set the language in this method? I currently have this but this is causing a StackOverflowError :
#Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
localeResolver.setLocale(request, response, locale);
}
Can someone help me? :D

Here is how I did it in my program, worked withut problems
#Bean
public LocaleResolver localeResolver() {
return new SessionLocaleResolver();
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new
LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
After that it should work with just calling it in url with ?lang=en

Related

After implementing Spring Session Management Spring security keeps forwarding me to the login page

I am working on JEE application. We recently switched from container based security to spring security. I am now trying move session handling out of the container and into the application using spring-session-jdbc. I've created the required tables in our database and created the following SessionConfig file:
#Configuration
#EnableJdbcHttpSession
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
#Bean
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName("java:jboss/MyDS");
bean.setProxyInterface(DataSource.class);
bean.setLookupOnStartup(false);
bean.afterPropertiesSet();
return (DataSource) bean.getObject();
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public FindByIndexNameSessionRepository<?> sessionRepository(PlatformTransactionManager txManager,
DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
return new JdbcIndexedSessionRepository(jdbcTemplate, txTemplate);
}
}
We have a security config where I autowire the the sessionRepository and use it to create the SessionAuthenticationStrategy like:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(SecurityConfig.class);
#Autowired
public FindByIndexNameSessionRepository<?> repo;
#Override
public void configure(WebSecurity web) throws Exception {
// put all static resource or external urls here
web.ignoring().antMatchers("/external/**", "/react/**", "/images/**", "/js/**", "/css/**",
"/vendor/**", "/fonts/**");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
String maxSessions =
GenericPropertiesReader.getInstance().getValue("config.logins.max.sessions");
http.sessionManagement()// set the session management
.sessionAuthenticationStrategy(sessionAuthenticationStrategy())
.invalidSessionUrl("/login.html") // no user session forward here
.maximumSessions(Integer.valueOf(maxSessions))// 1 or -1 for unlimited
.maxSessionsPreventsLogin(false)// new session will terminate old session and forward them
// to the log in page
.expiredUrl("/login.html?type=expired-session");
http.headers().frameOptions().disable();
http.authorizeRequests()// put any antMatchers after this
.antMatchers("/login.html").permitAll() // permit any login page
.anyRequest().authenticated().and().formLogin() // we are using form login
.loginPage("/login.html") // show our custom login form
.loginProcessingUrl("/login") // post to Spring's action URL so our custom auth provider is invoked
.successHandler(successHandler()).failureHandler(failureHandler())
.permitAll() // so anyone can see it
.and().logout().logoutUrl("/logout")
.logoutSuccessHandler(new MyLogoutSuccessHandler())// our custom logout handler
.invalidateHttpSession(true) // delete session, need more work??
.deleteCookies("JSESSIONID") // and get rid of that cookie so they can't auto-login again
.permitAll()
.and().x509().x509AuthenticationFilter(this.x509AuthFilter());
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(x509AuthProvider()).authenticationProvider(loginAuthProvider());
}
#Bean
public PreAuthenticatedAuthenticationProvider x509AuthProvider() {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(x509PreAuthUserDetailsService());
return provider;
}
#Bean // this irks me.
public AuthenticationManager myAuthenticationManager() throws Exception {
return this.authenticationManager();
}
#Bean
X509AuthenticationFilter x509AuthFilter() throws Exception {
X509AuthenticationFilter filter = new X509AuthenticationFilter();
filter.setAuthenticationSuccessHandler(x509SuccessHandler());
filter.setPrincipalExtractor(x509Extractor());
filter.setAuthenticationManager(this.myAuthenticationManager());
filter.setAuthenticationFailureHandler(failureHandler());
return filter;
}
#Bean
public X509PrincipalExtractor x509Extractor() {
return new MyX509DodIdExtractor();
}
#Bean
public MyX509PreAuthUserDetailsService x509PreAuthUserDetailsService() {
return new MyX509PreAuthUserDetailsService();
}
#Bean
public MyAuthenticationProvider loginAuthProvider() {
return new MyAuthenticationProvider ();
}
#Bean
MyAuthenticationSuccessHandler x509SuccessHandler() {
MyAuthenticationSuccessHandler handler = new MyAuthenticationSuccessHandler ();
handler.setForwardResonse(false);
return handler;
}
#Bean
public MyAuthenticationSuccessHandler successHandler() {
return new MyAuthenticationSuccessHandler();
}
#Bean
public MyAuthenticationFailureHandler failureHandler() {
MyAuthenticationFailureHandler failureHandler = new MyAuthenticationFailureHandler();
failureHandler.setExceptionMappings(LoginFailures.exceptionMap());
failureHandler.setDefaultFailureUrl("/login.html?login-failure=" + LoginFailures.DEFAULT.code);
return failureHandler;
}
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
#Bean
public SpringSessionBackedSessionRegistry<? extends Session> sessionRegistry()
throws IllegalArgumentException, NamingException {
return new SpringSessionBackedSessionRegistry<>(repo);
}
#Bean
public SessionAuthenticationStrategy sessionAuthenticationStrategy()
throws IllegalArgumentException, NamingException {
ConcurrentSessionControlAuthenticationStrategy sessionAuthenticationStrategy =
new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
return sessionAuthenticationStrategy;
}
}
I see the session tables in the database being filled when attempting to login. I never hit any of the authentication code when debugging. I just am redirected to the login page every time.
I feel like I must be missing something obvious. I was getting errors that there was no unique bean of type FindByIndexNameSessionRepository<?> until I changed the name of the bean in SessionConfig to sessionRepository. Which makes me think there's another bean of that type being instantiated by Spring (not in our code base) that might be interfering?

ThymeleafView: java.lang.IllegalArgumentException: Property 'locale' is required

I follow the below tutorial step by step to complete NO.10 Rendering Template Fragments.
https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#rendering-template-fragments
And I get an exception: nested exception is java.lang.IllegalArgumentException: Property 'locale' is required.
My code is below, Could someone tell me how to fix it? Thank you very much.
WebConfig.java
public class WebConfig implements WebMvcConfigurer {
#Autowired
private WebApplicationContext context;
#Bean
public ServletContextTemplateResolver templateResolver() {
final ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(context.getServletContext());
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
#Bean
public ThymeleafViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
#Bean("messageSource")
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("lang/messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
return new CookieLocaleResolver();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
registry.addInterceptor(localeChangeInterceptor);
}
#Bean(name = "content-part")
#Scope("prototype")
public ThymeleafView someViewBean() {
ThymeleafView view = new ThymeleafView("index"); // templateName = 'index'
view.setMarkupSelector("content");
return view;
}
}
HomeController.java
public class HomeController {
#RequestMapping("/showContentPart")
public String showContentPart(final Locale locale, HttpServletRequest request, HttpServletResponse response) {
return "content-part";
}
}
index.html
https://i.stack.imgur.com/WOGpR.png
Try this :
#RequestMapping("/showContentPart")
public class HomeController
{
public String showContentPart(final Locale locale, HttpServletRequest request, HttpServletResponse response) {
return "content-part";
}

Create custom LocalChangeInterceptor

I am writing an application using Spring Boot. In my application I am going to use localized URL smth like http://localhost:8080/School/**en**/xxx/... . I try to implement Custom Locale Interceptor. In listing below the first variant works fine. I would like to use the second variant but instead of France or Germany locale doesn't change and remains en_US. Can anybody please suggest me anything ?
#Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
1 VARIANT
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.GERMANY);
return slr;
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
2 VARIANT
#Bean
public LangInterceptor langInterceptor() {
return new LangInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor( langInterceptor());
}
#Bean
public SessionLocaleResolver sessionLocaleResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.GERMANY);
return sessionLocaleResolver;
}
}
And my custom Interceptor:
public class LangInterceptor extends HandlerInterceptorAdapter {
#Autowired
LocaleResolver sessionLocaleResolver;
Locale locale;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (StringUtils.startsWithIgnoreCase(request.getServletPath(), "/it/")) {
locale = new Locale("it");
} else {
locale = new Locale("en");
}
sessionLocaleResolver.setLocale(request, response, Locale.FRENCH);
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
You can't throw in a random HandlerInterceptorAdapter to change the locale. Spring uses AcceptHeaderLocaleResolver by default and you want to resolve based on path segment. Extend WebMvcConfigurerAdapter, then do the following:
#Bean
public LocaleResolver localeResolver() {
// return custom LocaleResolver
}
However, I'm cautioning you that you're going against the HTTP spec. The Accept-Language header is meant for this purpose, which is what the AcceptHeaderLocaleResolver uses.
You don't need a LocaleChangeInterceptor because it operates on a query parameter.
I found the solution. First variant will work if I just add in my configuration file LocaleChangeInterceptor. The reason looks like LocaleResolver doesn't initialize without interceptor, but spring docs says that every class has default constructor. So it works.
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}

sessionRegistry.getAllPrincipals() is empty

I trying to get all logged-in users using sessionRegistry in my Spring-MVC application, i found a lot of posts and answers on how to resolve it, but i could not fix it. I'm using configuration by annotation.
I'm new to spring mvc and i want to learn a best practice, so all comments about other configurations or about my code are welcome.
Here is my code
#Configuration
#ComponentScan(basePackages = {"com.uno"})
#Import({ SecurityConfig.class })
#EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public TilesViewResolver tilesViewResolver() {
TilesViewResolver resolver = new TilesViewResolver();
resolver.setViewClass(TilesView.class);
resolver.setOrder(1);
return resolver;
}
#Bean
public TilesConfigurer tilesConfigurer() {
TilesConfigurer tilesConfigurer = new TilesConfigurer();
tilesConfigurer.setCompleteAutoload(true);
tilesConfigurer.setCheckRefresh(true);
return tilesConfigurer;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations(
"/resources/");
}
#Bean
SessionFactory sessionFactory() {
org.hibernate.cfg.Configuration configuration = new org.hibernate.cfg.Configuration();
configuration.configure();
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(
dataSource());
builder.scanPackages("com.uno.domain").addProperties(
configuration.getProperties());
return builder.buildSessionFactory();
}
#Bean
public DriverManagerDataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/uno");
ds.setUsername("root");
return ds;
}
#Bean
public HibernateTransactionManager transactionManager() {
return new HibernateTransactionManager(sessionFactory());
}
#Bean
UserDao userDao() {
return new UserDaoImpl();
}
#Bean
UserService userService() {
return new UserServiceImpl();
}
#Bean
RoleDao roleDao() {
return new RoleDaoImpl();
}
#Bean
RoleService roleService() {
return new RoleServiceImpl();
}
#Bean
ConnexionSucessHandler connexionSuccessHandler() {
return new ConnexionSucessHandler();
}
#Bean
PersistentTokenRepository remmeberMeTokenRepository() {
JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
db.setDataSource(dataSource());
return db;
}
/* Localization section */
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
#Bean
LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(new Locale("en"));
return sessionLocaleResolver;
}
#Bean
LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
#Bean
ControllerClassNameHandlerMapping controllerClassNameHandlerMapping() {
ControllerClassNameHandlerMapping controllerClassNameHandlerMapping = new ControllerClassNameHandlerMapping();
Object[] interceptors = new Object[] { localeChangeInterceptor() };
controllerClassNameHandlerMapping.setInterceptors(interceptors);
return controllerClassNameHandlerMapping;
}
#Bean
ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
reloadableResourceBundleMessageSource.setBasename("resources/i18n/messages");
reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8");
return reloadableResourceBundleMessageSource;
}
/* Localization section */
#Bean
CommonsMultipartResolver filterMultipartResolver(){
return new CommonsMultipartResolver();
}
}
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userService")
UserService userDetailsService;
#Autowired
ConnexionSucessHandler connexionSucessHandler;
#Autowired
SessionRegistry sessionRegistry;
#Autowired
PersistentTokenRepository remmeberMeTokenRepository;
#Autowired
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlAuthenticationStrategy;
#Autowired
ConcurrentSessionFilter concurrentSessionFilter;
#Autowired
RegisterSessionAuthenticationStrategy registerSessionAuthenticationStrategy;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/mailManagement/**")
.hasAnyRole("USER", "ADMIN").antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.antMatchers("/login/**").permitAll().and().formLogin().successHandler(connexionSucessHandler)
.loginPage("/login").failureUrl("/login?error").usernameParameter("username")
.passwordParameter("password").and().logout().invalidateHttpSession(true).deleteCookies("JSESSIONID")
.logoutUrl("/logout").logoutSuccessUrl("/login?logout").and().csrf().and().exceptionHandling()
.accessDeniedPage("/403").and().rememberMe().rememberMeParameter("uno-remember-me")
.rememberMeCookieName("uno-remember-me").tokenValiditySeconds(1296000)
.tokenRepository(remmeberMeTokenRepository).and().sessionManagement()
.sessionAuthenticationStrategy(concurrentSessionControlAuthenticationStrategy).maximumSessions(-1);
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Bean
public ConcurrentSessionFilter concurrentSessionFilter(){
return new ConcurrentSessionFilter(sessionRegistry);
}
#Bean
public ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlAuthenticationStrategy(){
return new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry);
}
#Bean
public RegisterSessionAuthenticationStrategy registerSessionAuthenticationStrategy(){
return new RegisterSessionAuthenticationStrategy(sessionRegistry);
}
}
public class SecurityInitializer extends
AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
#Override
protected boolean enableHttpSessionEventPublisher() {
return true;
}
}
public class MvcWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
}
#Override
protected Class[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(new RequestContextListener());
super.onStartup(servletContext);
}
}
public class ConnexionSucessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
#Autowired
UserProfile userProfile;
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication auth) throws IOException,
ServletException {
RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
Collection authorities = auth.getAuthorities();
userProfile.loadUser(auth.getName());
for(GrantedAuthority grantedAuthority : authorities){
switch (grantedAuthority.getAuthority()) {
case "ROLE_ADMIN":
redirectStrategy.sendRedirect(request, response, "/admin");
break;
case "ROLE_USER":
redirectStrategy.sendRedirect(request, response, "/user");
break;
}
}
}
}
problem resolved.
I was loading the same configuration twice in both ContextLoaderListener and DispatcherServlet.

How can I have list of all users logged in (via spring security) my web application

I'm using spring security in my web application, and now I want to have a list of all users who are logged in my program.
How can I have access to that list? Aren't they already kept somewhere within spring framework? Like SecurityContextHolder or SecurityContextRepository?
For accessing the list of all logged in users you need to inject SessionRegistry instance to your bean.
#Autowired
#Qualifier("sessionRegistry")
private SessionRegistry sessionRegistry;
And then using injcted SessionRegistry you can access the list of all principals:
List<Object> principals = sessionRegistry.getAllPrincipals();
List<String> usersNamesList = new ArrayList<String>();
for (Object principal: principals) {
if (principal instanceof User) {
usersNamesList.add(((User) principal).getUsername());
}
}
But before injecting session registry you need to define session management part in your spring-security.xml (look at Session Management section in Spring Security reference documentation) and in concurrency-control section you should set alias for session registry object (session-registry-alias) by which you will inject it.
<security:http access-denied-page="/error403.jsp" use-expressions="true" auto-config="false">
<security:session-management session-fixation-protection="migrateSession" session-authentication-error-url="/login.jsp?authFailed=true">
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.html" session-registry-alias="sessionRegistry"/>
</security:session-management>
...
</security:http>
In JavaConfig, it would look like this:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
// ...
http.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
}
With the calling code looking like this:
public class UserController {
#Autowired
private SessionRegistry sessionRegistry;
public void listLoggedInUsers() {
final List<Object> allPrincipals = sessionRegistry.getAllPrincipals();
for(final Object principal : allPrincipals) {
if(principal instanceof SecurityUser) {
final SecurityUser user = (SecurityUser) principal;
// Do something with user
System.out.println(user);
}
}
}
}
Note that SecurityUser is my own class which implements UserDetails.
Please correct me if I'm wrong.
I think #Adam's answer is incomplete. I noticed that sessions already expired in the list were appearing again.
public class UserController {
#Autowired
private SessionRegistry sessionRegistry;
public void listLoggedInUsers() {
final List<Object> allPrincipals = sessionRegistry.getAllPrincipals();
for (final Object principal : allPrincipals) {
if (principal instanceof SecurityUser) {
final SecurityUser user = (SecurityUser) principal;
List<SessionInformation> activeUserSessions =
sessionRegistry.getAllSessions(principal,
/* includeExpiredSessions */ false); // Should not return null;
if (!activeUserSessions.isEmpty()) {
// Do something with user
System.out.println(user);
}
}
}
}
}
Hope it helps.
Please correct me if I'm wrong too.
I think #Adam's and #elysch`s answer is incomplete. I noticed that there are needed to add listener:
servletContext.addListener(HttpSessionEventPublisher.class);
to
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
...
servletContext.addListener(HttpSessionEventPublisher.class);
}
with security conf:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
// ...
http.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
And then you will get current online users!
You need to inject SessionRegistry (as mentioned eariler) and then you can do it in one pipeline like this:
public List<UserDetails> findAllLoggedInUsers() {
return sessionRegistry.getAllPrincipals()
.stream()
.filter(principal -> principal instanceof UserDetails)
.map(UserDetails.class::cast)
.collect(Collectors.toList());
}
Found this note to be quite important and relevant:
"[21] Authentication by mechanisms which perform a redirect after
authenticating (such as form-login) will not be detected by
SessionManagementFilter, as the filter will not be invoked during the
authenticating request. Session-management functionality has to be
handled separately in these cases."
https://docs.spring.io/spring-security/site/docs/3.1.x/reference/session-mgmt.html#d0e4399
Also, apparently a lot of people have troubles getting sessionRegistry.getAllPrincipals() returning something different from an empty array. In my case, I fixed it by adding the sessionAuthenticationStrategy to my custom authenticationFilter:
#Bean
public CustomUsernamePasswordAuthenticationFilter authenticationFilter() throws Exception {
...
authenticationFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
//cf. https://stackoverflow.com/questions/32463022/sessionregistry-is-empty-when-i-use-concurrentsessioncontrolauthenticationstrate
public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
List<SessionAuthenticationStrategy> stratList = new ArrayList<>();
SessionFixationProtectionStrategy concStrat = new SessionFixationProtectionStrategy();
stratList.add(concStrat);
RegisterSessionAuthenticationStrategy regStrat = new RegisterSessionAuthenticationStrategy(sessionRegistry());
stratList.add(regStrat);
CompositeSessionAuthenticationStrategy compStrat = new CompositeSessionAuthenticationStrategy(stratList);
return compStrat;
}
Similar to #rolyanos solution, mine for me always works:
- for the controller
#RequestMapping(value = "/admin")
public String admin(Map<String, Object> model) {
if(sessionRegistry.getAllPrincipals().size() != 0) {
logger.info("ACTIVE USER: " + sessionRegistry.getAllPrincipals().size());
model.put("activeuser", sessionRegistry.getAllPrincipals().size());
}
else
logger.warn("EMPTY" );
logger.debug(log_msg_a + " access ADMIN page. Access granted." + ANSI_RESET);
return "admin";
}
- for the front end
<tr th:each="activeuser, iterStat: ${activeuser}">
<th><b>Active users: </b></th> <td align="center" th:text="${activeuser}"></td>
</tr>
- for spring confing
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.logout()
.logoutSuccessUrl("/home")
.logoutUrl("/logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
http.authorizeRequests()
.antMatchers("/", "/home")
.permitAll()
.antMatchers("/admin")
.hasRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/home")
.defaultSuccessUrl("/main")
.permitAll()
.and()
.logout()
.permitAll();
http.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
http.authorizeRequests().antMatchers("/webjars/**").permitAll();
http.exceptionHandling().accessDeniedPage("/403");
}

Resources