There are multiple versions of the same questions around and none seem to address the issue. I would like to get online users from spring security. I understand we need to Autowire SessionRegistry
and use it. But still, it doesn't work. Here is the code.
Not sure whether it is due to custom Username, password authentication or due to custom password encoder or something else. Everything seems to be correct. Even getting the current logged in user's data works fine but not logged in user list.
SessionSecurityConfig.java
@EnableJpaRepositories(basePackageClasses = UsersRepository.class)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SessionSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordencoder;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private UsernamePasswordAuthProvider usernamepasswdauth;
@Bean
SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(usernamepasswdauth).userDetailsService(userDetailsService)
.passwordEncoder(passwordencoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
http.csrf().disable();
http.authorizeRequests() //
.antMatchers("/ua/*").permitAll() //
.antMatchers("/auth/*").authenticated() //
.and().requestCache() //
.requestCache(new NullRequestCache());
http.httpBasic().disable();
http.formLogin().disable();
http.logout().disable();
http
.sessionManagement()
.maximumSessions(1).sessionRegistry(sessionRegistry());
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
PasswordUpgrader.java
@Component
@Primary
public class PasswordUpgrader implements PasswordEncoder { // used to upgrade NTML password hashes to Bcrypt
private final static BCryptPasswordEncoder bcrypt = new BCryptPasswordEncoder();
private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
@Autowired
JdbcTemplate jdbc;
public String encode(CharSequence rawPassword) {
byte[] bytes = NtlmPasswordAuthentication.nTOWFv1(rawPassword.toString());
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars).toLowerCase();
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (encodedPassword == null || encodedPassword.length() == 0) {
return false;
}
if (encodedPassword.equals(encode(rawPassword))) {
String sql = "update user_data set password=? where password=?";
jdbc.update(sql, new Object[] { bcrypt.encode(rawPassword), encode(rawPassword) });
return true;
} else {
return bcrypt.matches(rawPassword, encodedPassword);
}
}
}
UsernamePasswordAuthProvider.java
@Component
public class UsernamePasswordAuthProvider implements AuthenticationProvider {
Log logger = LogFactory.getLog(getClass());
@Autowired
private PasswordEncoder passwordencoder;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
Userdata userdata;
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
String username = String.valueOf(auth.getPrincipal());
String password = String.valueOf(auth.getCredentials());
UserDetails user = userDetailsService.loadUserByUsername(username);
String encodedpassword = user.getPassword().toString();
logger.info("inside username passwd authentication");
if (encodedpassword != null && password != null && passwordencoder.matches(password, encodedpassword)) {
logger.info("inside username passwd authentication");
return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
}
throw new BadCredentialsException("Username/Password Incorrect");
}
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
UnauthController.java
@RestController
@RequestMapping("/ua")
public class UnauthController {
@Autowired
private UsernamePasswordAuthProvider usernamepasswdauth;
@PostMapping("/login")
public Map<String, Object> login(HttpServletRequest req, @RequestBody Map<String, Object> map) {
Authentication auth = usernamepasswdauth.authenticate(new UsernamePasswordAuthenticationToken(
map.get("username").toString(), map.get("password").toString()));
SecurityContextHolder.getContext().setAuthentication(auth);
map.put("sessionid", session.getId());
return map;
}
}
AuthController.java
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
Userdata user;
@Autowired
SessionRegistry sessionregistry;
Log logger = LogFactory.getLog(getClass());
@GetMapping("/onlineusers")
public List<String> authhello(Authentication authentication) {
logger.debug(user.getEmail()); // prints current logged in user's email.
logger.debug(sessionRegistry.getAllPrincipals());//returns empty
return sessionRegistry.getAllPrincipals().stream()
.filter(u -> !sessionRegistry.getAllSessions(u, false).isEmpty()).map(Object::toString)
.collect(Collectors.toList());
}
}
Tried Approaches: