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结束。

实际上我也考虑过,把增删改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<String> excludeUrl = Arrays.asList("/api/login/**","/api/event/insert");
        registry.addInterceptor(new SessionInterceptor()).addPathPatterns("/**")
        .excludePathPatterns(excludeUrl);
    }
  1. 增删改或者admin api,通过HttpServletRequest和HttpServletResponse进行权限检查和权限不足时的返回处理
	@PostMapping(value="/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);
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章