權限管理是個很大很複雜的模塊,目前,剛開始做,就化繁爲簡,簡單的切分成前端控件控制,和後端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,這是可行的。合適的話,以後往這個方向做。
-
在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<String> excludeUrl = Arrays.asList("/api/login/**","/api/event/insert");
registry.addInterceptor(new SessionInterceptor()).addPathPatterns("/**")
.excludePathPatterns(excludeUrl);
}
- 增刪改或者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);
});