【发布时间】:2021-02-18 22:12:47
【问题描述】:
相同的问题有多个版本,但似乎没有一个可以解决这个问题。我想从spring security获得在线用户。我知道我们需要 Autowire SessionRegistry 并使用它。但是,它仍然不起作用。这是代码。
不确定是由于自定义用户名、密码身份验证还是由于自定义密码编码器或其他原因。一切似乎都是正确的。即使获取当前登录用户的数据也可以正常工作,但未登录用户列表。
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);
}
}
}
用户名密码AuthProvider.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());
}
}
尝试过的方法:
【问题讨论】:
-
您找到解决方案了吗?
-
@It'sK 通过在 Redis 中创建和存储一个集合并在用户登录时向其中添加用户名来应用解决方法。在 15 分钟内使用了一次调度程序来检查会话到期并从集合中删除用户名。仍在等待适当的修复。
-
我遇到过,当您使用自定义身份验证并自己重定向经过身份验证的用户时,它不会调用 spring session 过滤器,因此它不会自行处理,我们必须对其进行配置( sessionManagement 下的 sessionAuthenticationStrategy 和 expiredSessionStrategy)。我已经设置了它,它现在就像魅力一样。可能我稍后会写答案以帮助其他人。
-
@It'sK 这将是一个很大的帮助。另外,如果您可以在 GitHub 上发布该项目并添加一个链接,那将是很棒的教程。
-
已经计划好了。这些配置我也很挣扎,所以我很乐意提供帮助。
标签: java spring spring-boot spring-security