我正在使用spring boot,spring secuirity和spring session(redis)构建一个spring REST Web应用程序。我正在使用spring cloud和zuul proxy建立一个遵循网关模式的云应用程序。在这种模式中,我使用spring会话来管理redis中的HttpSesssion并使用它来授权我的资源服务器上的请求。当执行一个改变会话权限的操作时,我想更新该对象,以便用户不必注销以反映更新。有人有解决方案吗?
答案 0 :(得分: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_SECURITY_CONTEXT”属性并更新SecurityContextHolder中的身份验证。 用户无需退出。在指定过滤器的顺序时,重要的是它在SpringSessionRepositoryFilter之后。该对象的@Order为-2147483598所以我只是将我的过滤器改为1,以确保它是下一个被执行的过滤器。
工作流程如下:
下次用户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);
}