Spring Session Redis 和Spring Security 如何更新用户session?

Spring Session Redis and Spring Security how to update user session?

我正在使用 spring 引导、spring 安全和 spring 会话 (redis) 构建 spring REST Web 应用程序。我正在使用 spring 云和 zuul 代理按照网关模式构建云应用程序。在这个模式中,我使用 spring 会话来管理 redis 中的 HttpSession 并使用它来授权我的资源服务器上的请求。当执行更改会话权限的操作时,我想更新该对象,以便用户不必注销即可反映更新。有人对此有解决方案吗?

要更新权限,您需要在两个地方修改身份验证对象。一个在安全上下文中,另一个在请求上下文中。您的主体对象将是 org.springframework.security.core.userdetails.User 或扩展 class(如果您已覆盖 UserDetailsS​​ervice)。这适用于修改当前用户。

    Authentication newAuth = new UsernamePasswordAuthenticationToken({YourPrincipalObject},null,List<? extends GrantedAuthority>)

    SecurityContextHolder.getContext().setAuthentication(newAuth);
    RequestContextHolder.currentRequestAttributes().setAttribute("SPRING_SECURITY_CONTEXT", newAuth, RequestAttributes.SCOPE_GLOBAL_SESSION);

要使用 spring 会话为任何登录用户更新会话,需要自定义过滤器。过滤器存储一组已被某些进程修改的会话。当需要修改新会话时,消息传递系统会更新该值。当请求具有匹配的会话密钥时,过滤器会在数据库中查找用户以获取更新。然后它更新会话上的 "SPRING_SECURITY_CONTEXT" 属性 并更新 SecurityContextHolder 中的身份验证。 用户不需要注销。当指定过滤器的顺序时,重要的是它在 SpringSessionRepositoryFilter 之后。该对象的 @Order 为 -2147483598,因此我只是将过滤器更改为一个以确保它是下一个执行的过滤器。

工作流程如下:

  1. 修改用户A权限
  2. 向过滤器发送消息
  3. 添加要设置的用户 A 会话密钥(在过滤器中)
  4. 下次用户 A 通过过滤器时,更新他们的会话

    @Component
    @Order(UpdateAuthFilter.ORDER_AFTER_SPRING_SESSION)
    public class UpdateAuthFilter extends OncePerRequestFilter
    {
    public static final int ORDER_AFTER_SPRING_SESSION = -2147483597;
    
    private Logger log = LoggerFactory.getLogger(this.getClass());
    
    private Set<String> permissionsToUpdate = new HashSet<>();
    
    @Autowired
    private UserJPARepository userJPARepository;
    
    private void modifySessionSet(String sessionKey, boolean add)
    {
        if (add) {
            permissionsToUpdate.add(sessionKey);
        } else {
            permissionsToUpdate.remove(sessionKey);
        }
    }
    
    public void addUserSessionsToSet(UpdateUserSessionMessage updateUserSessionMessage)
    {
        log.info("UPDATE_USER_SESSION - {} - received", updateUserSessionMessage.getUuid().toString());
        updateUserSessionMessage.getSessionKeys().forEach(sessionKey -> modifySessionSet(sessionKey, true));
        //clear keys for sessions not in redis
        log.info("UPDATE_USER_SESSION - {} - success", updateUserSessionMessage.getUuid().toString());
    }
    
    @Override
    public void destroy()
    {
    
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException
    {
        HttpSession session = httpServletRequest.getSession();
    
    if (session != null)
    {
        String sessionId = session.getId();
        if (permissionsToUpdate.contains(sessionId))
        {
            try
            {
                SecurityContextImpl securityContextImpl = (SecurityContextImpl) session.getAttribute("SPRING_SECURITY_CONTEXT");
                if (securityContextImpl != null)
                {
                    Authentication auth = securityContextImpl.getAuthentication();
                    Optional<User> user = auth != null
                                          ? userJPARepository.findByUsername(auth.getName())
                                          : Optional.empty();
    
                    if (user.isPresent())
                    {
                        user.get().getAccessControls().forEach(ac -> ac.setUsers(null));
    
                        MyCustomUser myCustomUser = new MyCustomUser (user.get().getUsername(),
                                                                     user.get().getPassword(),
                                                                     user.get().getAccessControls(),
                                                                     user.get().getOrganization().getId());
    
                        final Authentication newAuth = new UsernamePasswordAuthenticationToken(myCustomUser ,
                                                                                               null,
                                                                                               user.get().getAccessControls());
                        SecurityContextHolder.getContext().setAuthentication(newAuth);
                        session.setAttribute("SPRING_SECURITY_CONTEXT", newAuth);
                    }
                    else
                    {
                        //invalidate the session if the user could not be found
                        session.invalidate();
                    }
                }
                else
                {
                    //invalidate the session if the user could not be found
                    session.invalidate();
                }
            }
            finally
            {
                modifySessionSet(sessionId, false);
            }
        }
    }
    
    filterChain.doFilter(httpServletRequest, httpServletResponse);
    }