权限管理是个很大很复杂的模块,目前,刚开始做,就化繁为简,简单的切分成前端控件控制,和后端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,这是可行的。合适的话,以后往这个方向做。
- 在springboot的property文件里配置上session过期时间(注意:springboot 2.x以前的版本里,这个配置项的名字不一样的)
server.servlet.session.timeout=300s
- 用户登录成功之后,可以往
request.getSession().setAttributes("xxx", "...")
塞一些token之类的。同时这个token也保存在服务端的某个数据结构里。
- 做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;
}
}
}
- 将定义好的拦截器加入到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);
}
- 增删改或者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);
});