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

时间:2015-11-30 01:37:08

标签: spring spring-security redis spring-session spring-web

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

1 个答案:

答案 0 :(得分: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_SECURITY_CONTEXT”属性并更新SecurityContextHolder中的身份验证。 用户无需退出。在指定过滤器的顺序时,重要的是它在SpringSessionRepositoryFilter之后。该对象的@Order为-2147483598所以我只是将我的过滤器改为1,以确保它是下一个被执行的过滤器。

工作流程如下:

  1. 修改用户权限
  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);
    }