Java 如何在spring security中更新用户权限后立即启用权限?

Java 如何在spring security中更新用户权限后立即启用权限?,java,spring-security,Java,Spring Security,我正在使用spring security framework。当我更新权限时,它不会立即生效。我必须退出当前用户(意味着注销),然后重新访问(意味着登录)以更新用户的权限 是一种在spring security中更新用户权限后立即启用权限的方法吗?由于您没有在问题中提供确切的细节,我假设您遇到以下情况: 您正在提供UserDetails服务,以便在用户尝试登录时加载UserDetails 作为该服务的一部分,您正在查询数据库/DAO以加载有关用户权限的详细信息,并基于此设置授予的权限 当你说“当

我正在使用spring security framework。当我更新权限时,它不会立即生效。我必须退出当前用户(意味着注销),然后重新访问(意味着登录)以更新用户的权限


是一种在spring security中更新用户权限后立即启用权限的方法吗?

由于您没有在问题中提供确切的细节,我假设您遇到以下情况:

  • 您正在提供UserDetails服务,以便在用户尝试登录时加载UserDetails
  • 作为该服务的一部分,您正在查询数据库/DAO以加载有关用户权限的详细信息,并基于此设置授予的权限
  • 当你说“当我更新权限时”,你指的是更新用户在数据库中的权限(或任何你正在存储数据的地方)
  • 如果是这样的话,那么您所看到的是通过设计-Spring Security仅在用户首次尝试登录时为其加载UserDetails,然后从那时起将其存储在会话中。一般来说,这是有意义的,因为它避免了应用程序必须在每个请求上执行相同的关于用户详细信息的查询。此外,在99.9%的访问中,用户的权限通常不会改变


    要更改此行为,您可能希望在某个位置添加一个“刷新”命令/页面,该命令/页面将触发一些代码(您必须编写这些代码),这些代码将重新查询UserDetails服务并替换SecurityContext中的UserDetails。我不相信有任何内置方法可以做到这一点。

    您可以创建自己的UserDetails接口实现,该接口从身份验证机制返回,允许访问授权机构[]。然后将您的新权限直接添加到该UserDetails对象。如果一个用户可以同时打开多个会话(或者如果多个用户共享同一个通用登录名),您可能会遇到问题,即新权限将仅在您更改的会话上显示。

    Gandalf解决方案有效但不完整。为了让spring security考虑新的权限(例如允许访问以前不可用的页面),您需要创建一个包含新权限列表的新身份验证对象(例如新用户名PasswordAuthenticationToken)。

    您可以在 像这样

    <bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
            <property name="alwaysReauthenticate" value="true"/>
     ...
    </bean>
    
    
    ...
    
    当然你应该注意,因为99.9%你不需要重新验证。由于身份验证可能使用数据库或其他东西,您的性能可能会降低。但是通常你有一个缓存,比如hibernate的第二级缓存,所以每次加载userdetails都应该是一个内存操作,在所有权限没有改变的情况下

    你可以试试这个

    SecurityContextHolder.getContext().setAuthentication(SecurityContextHolder.getContext().getAuthentication());
    

    不足以重置线程本地上下文,您还需要更新会话:

    UserContext userContext = (UserContext) context.getAuthentication().getPrincipal();
    if (userContext != null && userContext.getUsername() != null) {
        //This is my function to refresh the user details
        UserDetailsBean userDetails = getUserDetailsBean(userContext.getUsername());
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication instanceof UsernamePasswordAuthenticationToken) {
            UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
            auth.setDetails(userDetails);
        }
        return userDetails;
    } else {
        throw new ServiceException("User not authenticated");
    }
    request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
    

    至少在google appengine中,会话不是引用,通过修改线程本地不会自动更新,您必须手动设置会话。设置您的对象。

    以下线程解释了如何在每个请求中重新加载用户/主体:



    完整披露我是上述链接中问题的问答的作者。

    您的假设是正确的。我想知道有没有什么好方法可以做到这一点而不必重新登录。也许我可以修改存储在当前会话中的用户权限。我正在尝试..如果我想保护bean上的方法,我是否需要对另一个筛选器进行相同的更改,或者这就足够了?据我所知,此筛选器仅适用于web请求。如果您使用的是MEthodSecurityInterceptor,您可以自己调用重新验证。但是,如果每个方法调用之前都检查此web筛选器,因为每个方法调用都在http请求中,那么您应该知道这是正确的答案。我只想补充一点,需要通过节内的
    或通过拦截器属性将此拦截器合并到安全配置中:
    我尝试了此代码,但不幸的是,它并没有产生任何影响。Spring Source博客上的帖子中描述了一种可能的解决方案。这花了一些时间,但我认为这最终得到了解决: