6

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

4

1 回答 1

8

要更新权限,您需要在两个地方修改身份验证对象。一个在安全上下文中,另一个在请求上下文中。您的主体对象将是 org.springframework.security.core.userdetails.User 或扩展该类(如果您已覆盖 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 中的 Authentication。用户不需要注销。在指定过滤器的顺序时,重要的是它位于 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);
    }
    
于 2015-11-30T01:46:49.940 回答