Spring Security与会话get online的用户返回为空



有多个版本的相同问题,但似乎没有一个解决这个问题。我想让网上用户从春季安全。我知道我们需要自动连接SessionRegistry并使用它。但它仍然不起作用。这是代码。不确定是由于自定义用户名、密码身份验证还是由于自定义密码编码器或其他原因。一切似乎都是正确的。即使获取当前登录用户的数据也可以正常工作,但不能登录用户列表。

会话安全配置.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();
}
}

密码Upgrader.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());
}
}

尝试过的方法:

  1. Baeldung
  2. 堆栈溢出
  3. StackOverflow

如果您仔细阅读这里的文档,它写得很好(尽管非常隐蔽(。问题的原因在于身份验证后处理数据的方式。在spring安全性提供的默认身份验证中,成功身份验证后,控制将通过管理会话的筛选器传递。然而,如果您使用自定义身份验证并在成功身份验证后重定向用户,则过滤器不会起作用,这就是为什么在会话注册表中不添加会话,并且返回空列表的原因。

解决方案是将带有会话注册表的身份验证策略设置到spring安全的会话管理配置中。这将导致预期的行为。您会发现代码更有帮助。


方法1:

会话的春季安全配置

http
.sessionManagement()
.sessionAuthenticationStrategy(concurrentSession())
.maximumSessions(-1)
.expiredSessionStrategy(sessionInformationExpiredStrategy())

定义的bean

@Bean
public CompositeSessionAuthenticationStrategy concurrentSession() {
ConcurrentSessionControlAuthenticationStrategy concurrentAuthenticationStrategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
List<SessionAuthenticationStrategy> delegateStrategies = new ArrayList<SessionAuthenticationStrategy>();
delegateStrategies.add(concurrentAuthenticationStrategy);
delegateStrategies.add(new SessionFixationProtectionStrategy());
delegateStrategies.add(new RegisterSessionAuthenticationStrategy(sessionRegistry()));
return new CompositeSessionAuthenticationStrategy(delegateStrategies);
}

@Bean
SessionInformationExpiredStrategy sessionInformationExpiredStrategy() {
return new CustomSessionInformationExpiredStrategy("/login");
}

@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}

这是CustomSessionInformation ExpiredStrategy.java

public class CustomSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
private String expiredUrl = "";
public CustomSessionInformationExpiredStrategy(String expiredUrl) {
this.expiredUrl = expiredUrl;
}
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent sessionInformationExpiredEvent) throws IOException, ServletException {
HttpServletRequest request = sessionInformationExpiredEvent.getRequest();
HttpServletResponse response = sessionInformationExpiredEvent.getResponse();
request.getSession();// creates a new session
response.sendRedirect(request.getContextPath() + expiredUrl);
}
}

方法:2

在spring安全配置中,使用方法1中的concurrentSession((。

http.sessionManagement().sessionAuthenticationStrategy(concurrentSession());
http.addFilterBefore(concurrentSessionFilter(), ConcurrentSessionFilter.class);

这里是CustomConcurrentSessionFilter.java

public class CustomConcurrentSessionFilter extends ConcurrentSessionFilter {
public CustomConcurrentSessionFilter(SessionRegistry sessionRegistry) {
super(sessionRegistry);
}
public CustomConcurrentSessionFilter(SessionRegistry sessionRegistry, SessionInformationExpiredStrategy sessionInformationExpiredStrategy) {
super(sessionRegistry, sessionInformationExpiredStrategy);
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
super.doFilter(req, res, chain);
}
}

还在挠头找什么?在Github repo中查找工作示例。随意提出问题或发表意见。

最新更新