如何使用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
关键点 – 您应该能够访问用户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("/**"); } }
我也写了一篇关于这个的文章 。