老系统,记录一个 session 使用问题导致的鉴权问题

望舒的头像
望舒
标签:
session 缓存问题请求拦截器鉴权ThreadLocal

维护一个老系统,发现因后端拦截器 session 使用不当,导致用户退出登录后鉴权信息未被清除,引起后续接口鉴权问题。属于一个很容易疏忽的问题,以作记录

问题描述

端 session 使用不当,导致用户退出登录后鉴权信息未被清除,引起后续接口鉴权问题

伪代码复现

在系统统一的请求拦截器通过读取请求 header 中的 token 鉴权信息,解析出用户角色信息放在 session 中方便后续读取

伪代码如下:

复制
展开
public class Test implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (StrTool.isNotBlank(token)) {
            request.getSession().setAttribute("Token", token);
            request.getSession().setAttribute("User", UserTool.getUserFromToken(token));
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
    
}

解决方式

我们都知道 session 缓存是一次会话中的缓存,也就意味着如果页面没有刷新、sessionId 没有更改,退出登录后 session 缓存中的 User 信息没有被清除

而后续的自定义接口鉴权注解是通过读取 session 中的 User session 缓存作鉴权,从而导致越权问题

由于前端在退出登陆时只是清除了一下用户信息、token 信息,在不做很多变更的前提下,只能在后端的拦截器中二次判断一下 token 不存在就主动删除当前 session 缓存中的 User 信息

复制
展开
public class Test implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (StrTool.isNotBlank(token)) {
            request.getSession().setAttribute("Token", token);
            request.getSession().setAttribute("User", UserTool.getUserFromToken(token));
        } else {
            // 原有代码未作主动删除,这里每次都校验并清除一下 session 缓存
            request.getSession().removeAttribute("Token");
            request.getSession().removeAttribute("User");
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
    
}

其实可以利用 ThreadLocal 做一个 UserHolder 也可以更好地控制这些问题,比如:

复制
展开
public class UserHolder {

    private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();

    public static void setUser(User user) {
        userThreadLocal.set(user);
    }

    public static User getUser() {
        return userThreadLocal.get();
    }

    public static void clear() {
        userThreadLocal.remove();
    }
}

作者:https://blog.xn--rpv331d.com/望舒

链接:https://blog.xn--rpv331d.com/望舒/blog/129

转载请保留文章出处...

No data
No data