Vue + SpringBoot项目权限及Session管理

权限管理是个很大很复杂的模块,目前,刚开始做,就化繁为简,简单的切分成前端控件控制,和后端API权限控制。好记性不如烂笔头,整理一遍思路,慢慢沉淀。
1. 前端控件权限控制
将用户跟Role挂钩,admin role, read-only role等等,前端一些增删改控件以及Admin页面是否加载的话,可以使用v-if=editAccess, 查询用户是否有权限的过程(发get请求到服务端去查)可以放在组件的created hook里面,有权限则this.editAccess=true,反之false.
2. 后端API权限控制
[ http request 拦截器 springboot 2.x及以后] 后端API控制稍微麻烦些,要联合登录以及session过期等一起来做。确保session没过期,session是合法的,然后才进入到具体的api。对于一些敏感api(例如增删改api,或者只能admin角色才能做的一些api),干实事儿之前先进行权限检查,没权限的直接response.sendError(...)然后就return结束。
【Vue + SpringBoot项目权限及Session管理】实际上我也考虑过,把增删改api的权限检查拿出来,做成一个或者多个拦截器。这么一来的话,要把api分类,同一个权限级别的api可以放到一个拦截器里去。Anyway,这是可行的。合适的话,以后往这个方向做。

  1. 在springboot的property文件里配置上session过期时间(注意:springboot 2.x以前的版本里,这个配置项的名字不一样的)server.servlet.session.timeout=300s
  2. 用户登录成功之后,可以往request.getSession().setAttributes("xxx", "...")塞一些token之类的。同时这个token也保存在服务端的某个数据结构里。
  3. 做request拦截器,每一个request进来,先进拦截器,拦截器返回true才可以继请求api(加了多个拦截器的话,会按照添加顺序进到下一个拦截器里面)。拦截器返回false的话呢,就不会继续了。
    这个拦截器可以通过继承HandlerInterceptorAdapter来实现。父类里有这三个方法:
    preHandle: 执行具体api之前
    postHandle: api执行后
    afterCompletion: 前端收到返回之后
    目前我的需求,只需要override preHandle就好了,如下所示:先验证session是否过期,然后验证session是否合法。
public class SessionInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // session expire if (request.getSession(false) == null) { response.sendError(HttpStatus.SC_UNAUTHORIZED, "Session time out, please re-login"); return false; } // validate token in session else if (request.getSession().getAttribute(token) != null) { if (token is valid) { return true; } else { response.sendError(HttpStatus.SC_UNAUTHORIZED, "Invalid token, please re-login"); return false; } } // invalid session else { response.sendError(HttpStatus.SC_UNAUTHORIZED, "Invalid session, please re-login"); return false; } } }

  1. 将定义好的拦截器加入到WebMvcConfigurer里面去,如下所示。需要注意的是,要先addPathPatterns("…")然后再去添加excludePathPatterns(也就是白名单,这些api的request进来不走拦截器),否则白名单不生效…
@Configuration public class WebMvcConfig implements WebMvcConfigurer@Override public void addInterceptors(InterceptorRegistry registry) { List excludeUrl = Arrays.asList("/api/login/**","/api/event/insert"); registry.addInterceptor(new SessionInterceptor()).addPathPatterns("/**") .excludePathPatterns(excludeUrl); }

  1. 增删改或者admin api,通过HttpServletRequest和HttpServletResponse进行权限检查和权限不足时的返回处理
@PostMapping(value="https://www.it610.com/editcase",produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public TestCaseInfo editCase(@RequestBody TestCaseInfo caseInfo, HttpServletRequest request, HttpServletResponse response) throws IOException{ if(user in current session does not have access){ response.sendError(HttpStatus.SC_FORBIDDEN, "Current user does not have access!"); return null; } do what should be done if user has access balabalabala... }

[ axios response 拦截器] 前端在收到服务端的返回之后,也可以做一个统一的预处理,判断一下,如果返回的是401证明Session过期或者不合法,要重新登录。如果返回的是403证明没权限等等。具体的HTTP Status Code和它的处理方式,结合code本身的意义以及业务场景来决定,前后端商量好就行。
感觉前端加response拦截器比后端简单很多啊…
this.$axios.interceptors.response.use(response => { return response; }, err => { if (err.response.status === 401) { this.$message.error(err.response.statusText + ", please re-login!"); this.$router.push("/login"); } else if (err.response.status === 403) { this.$message.error(err.response.statusText + ". User does not have access!"); } else { console.log('err', err.response); } return Promise.reject(err); });

    推荐阅读