我正在使用 spring boot、spring secuirity 和 spring session (redis) 构建一个 spring REST web 应用程序。我正在使用 spring cloud 和 zuul 代理按照网关模式构建云应用程序。在这个模式中,我使用 spring session 来管理 redis 中的 HttpSesssion 并使用它来授权我的资源服务器上的请求。当执行更改会话权限的操作时,我想更新该对象,以便用户不必注销即可反映更新。有人对此有解决方案吗?
问问题
6522 次
1 回答
8
要更新权限,您需要在两个地方修改身份验证对象。一个在安全上下文中,另一个在请求上下文中。您的主体对象将是 org.springframework.security.core.userdetails.User 或扩展该类(如果您已覆盖 UserDetailsService)。这适用于修改当前用户。
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,因此我只是将过滤器更改了一个以确保它是下一个执行的过滤器。
工作流程如下所示:
- 修改用户 A 权限
- 发送消息到过滤器
- 添加要设置的用户 A 会话密钥(在过滤器中)
下次用户 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 回答