如何使用Spring Security重新加载用户更新的权限

我正在使用Spring Security通过OpenID进行身份validation的应用程序。 当用户登录时,会话中会加载某些权限。

我有完全权限的用户,可以修改其他用户的权限(撤销,添加角色)。 我的问题是,如何动态更改用户会话权限? (不能使用SecurityContextHolder,因为我想更改另一个用户会话)。

简单方法:使用户会话无效,但如何? 更好的方法:刷新用户会话与新权限,但如何?

如果您需要动态更新登录用户的权限(当这些权限因任何原因而发生更改时),而无需注销并登录,您只需要在Spring SecurityContextHolder重置Authentication对象(安全令牌)。

例:

 Authentication auth = SecurityContextHolder.getContext().getAuthentication(); List updatedAuthorities = new ArrayList<>(auth.getAuthorities()); updatedAuthorities.add(...); //add your role here [eg, new SimpleGrantedAuthority("ROLE_NEW_ROLE")] Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), updatedAuthorities); SecurityContextHolder.getContext().setAuthentication(newAuth); 

谢谢,帮帮我了! 使用SessionRegistry ,我可以使用getAllPrincipals()来比较要修改的用户与会话中的当前活动用户。 如果存在会话,我可以使用以下命令使其会话无效: expireNow() (来自SessionInformation )以强制重新进行身份validation。

但我不明白securityContextPersistenceFilter的用处?

编辑:

 // user object = User currently updated // invalidate user session List loggedUsers = sessionRegistry.getAllPrincipals(); for (Object principal : loggedUsers) { if(principal instanceof User) { final User loggedUser = (User) principal; if(user.getUsername().equals(loggedUser.getUsername())) { List sessionsInfo = sessionRegistry.getAllSessions(principal, false); if(null != sessionsInfo && sessionsInfo.size() > 0) { for (SessionInformation sessionInformation : sessionsInfo) { LOGGER.info("Exprire now :" + sessionInformation.getSessionId()); sessionInformation.expireNow(); sessionRegistry.removeSessionInformation(sessionInformation.getSessionId()); // User is not forced to re-logging } } } } } 

关键点 – 您应该能够访问用户SecurityContext

如果您在servlet环境中并且在securityContextRepository中使用HttpSession作为securityContextPersistenceFilter ,则可以使用spring的SessionRegistry 。 强制用户重新授权(它应该比静默权限撤销更好)使他的HttpSession无效。 不要忘记将HttpSessionEventPublisher添加到web.xml

   org.springframework.security.web.session.HttpSessionEventPublisher   

如果您使用的是线程本地securityContextRepository ,那么您应该将自定义filter添加到springSecurityFilterChain以管理SecurityContext的注册表。 为此,您必须使用plain-bean springSecurityFilterChain配置(没有security命名空间快捷方式)。 使用带有自定义filter的普通bean配置,您可以完全控制身份validation和授权。

有些链接,它们并不能完全解决您的问题(没有OpenID),但可能有用:

  • 用于servlet环境的NIH会话注册表
  • 它是普通bean spring配置工作示例
  • 用于X.509 auth的真实普通bean spring配置 ,您可以从它开始并修改它以使用OpenID而不是X.509。

如果有人仍在研究如何在不强制该用户重新进行身份validation的情况下更新其他用户的权限,则可以尝试添加重新加载身份validation的拦截器。 这将确保您的权限始终更新。

但是 – 由于额外的拦截器,会产生一些性能影响(例如,如果从数据库中获取用户角色,则会查询每个HTTP请求)。

 @Component public class VerifyAccessInterceptor implements HandlerInterceptor { // ... @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); Set authorities = new HashSet<>(); if (auth.isAuthenticated()) { authorities.add(new SimpleGrantedAuthority("ROLE_USER")); } User userFromDatabase = getUserFromDatabase(auth.getName()); if (userFromDatabase != null) { // add whatever authorities you want here authorities.add(new SimpleGrantedAuthority("...")); } Authentication newAuth = null; if (auth.getClass() == OAuth2AuthenticationToken.class) { OAuth2User principal = ((OAuth2AuthenticationToken)auth).getPrincipal(); if (principal != null) { newAuth = new OAuth2AuthenticationToken(principal, authorities,(((OAuth2AuthenticationToken)auth).getAuthorizedClientRegistrationId())); } } SecurityContextHolder.getContext().setAuthentication(newAuth); return true; } } 

此特定实现使用OAuth2( OAuth2AuthenticationToken ),但您可以使用UsernamePasswordAuthenticationToken

现在,要将拦截器添加到配置中:

 @Configuration public class WebConfiguration extends WebMvcConfigurationSupport { @Autowired private VerifyAccessInterceptor verifyAccessInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(verifyAccessInterceptor).addPathPatterns("/**"); } } 

我也写了一篇关于这个的文章 。