先描述一下應(yīng)用場(chǎng)景汤纸,基于Spring MVC的WEB程序,需要對(duì)每個(gè)Action進(jìn)行權(quán)限判斷惯驼,當(dāng)前用戶有權(quán)限則允許執(zhí)行Action蹲嚣,無權(quán)限要出錯(cuò)提示。權(quán)限有很多種祟牲,比如用戶管理權(quán)限隙畜、日志審計(jì)權(quán)限、系統(tǒng)配置權(quán)限等等说贝,每種權(quán)限還會(huì)帶參數(shù)议惰,比如各個(gè)權(quán)限還要區(qū)分讀權(quán)限還是寫權(quán)限。
想實(shí)現(xiàn)統(tǒng)一的權(quán)限檢查乡恕,就要對(duì)Action進(jìn)行攔截言询,一般是通過攔截器來做,可以實(shí)現(xiàn)HandlerInterceptor或者HandlerInterceptorAdapter傲宜,但是每個(gè)Action都有不同的權(quán)限檢查运杭,比如getUsers要用戶管理的讀權(quán)限,deleteLogs要日志審計(jì)的寫權(quán)限函卒,只定義一個(gè)攔截器很難做到辆憔,為每種權(quán)限定義一個(gè)攔截器又太亂,此時(shí)可以通過自定義注解來標(biāo)明每個(gè)Action需要什么權(quán)限报嵌,然后在單一的攔截器里就可以統(tǒng)一檢查了虱咧。
具體這么做,先實(shí)現(xiàn)一個(gè)自定義注解锚国,名叫AuthCheck:
package com.lwq.controller;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {
/**
? ? * 權(quán)限類型
? ? * @return
? ? */
? ? String type()default "";
/**
? ? * 是否需要寫權(quán)限
? ? * @return
? ? */
? ? boolean write()default false;
}
具體可根據(jù)需要來定義類型腕巡,此處只建立兩個(gè)
這個(gè)注解里包含2個(gè)屬性,分別用于標(biāo)定權(quán)限的類型與讀寫要求血筑。然后為需要檢查權(quán)限的Action加注解绘沉,此處以getUsers和deleteLogs為例,前者要求對(duì)用戶管理有讀權(quán)限豺总,后者要求對(duì)日志審計(jì)有寫權(quán)限梆砸,注意@AuthCheck的用法:
然后定義權(quán)限攔截代碼
package com.lwq.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 全局?jǐn)r截器
*/
public class ActionTestInterceptorimplements HandlerInterceptor {
/**
? ? * 前置攔截,用于檢查身份與權(quán)限
? ? */
? ? @Override
? ? public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
//從傳入的handler中檢查是否有AuthCheck的聲明
? ? ? ? HandlerMethod method = (HandlerMethod)handler;
AuthCheck auth = method.getMethodAnnotation(AuthCheck.class);
//找到了园欣,取出定義的權(quán)限屬性,結(jié)合身份信息進(jìn)行檢查
if(auth !=null) {
????????????String type = auth.type();
????????????boolean write = auth.write();
????????????//根據(jù)type與write休蟹,結(jié)合session/cookie等身份信息進(jìn)行檢查
? ? ? ? ? ? //如果權(quán)限檢查不通過沸枯,可以輸出特定信息日矫、進(jìn)行跳轉(zhuǎn)等操作
? ? ? ? ? ? //并且一定要return false,表示被攔截的方法不用繼續(xù)執(zhí)行了
? ? ? ? ? ? if(!"user".equals(type)){
????????????????????//具體根據(jù)業(yè)務(wù)去查詢绑榴,比如數(shù)據(jù)查詢出來之后與type和write進(jìn)行比較哪轿,注意:對(duì)比失敗一定要返回false
????????????????????return false;
????????????}
}
//檢查通過,返回true翔怎,方法會(huì)繼續(xù)執(zhí)行
? ? ? ? return true;
}
@Override
? ? public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView model)throws Exception {
}
@Override
? ? public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception)throws Exception {
}
}
最后要實(shí)現(xiàn)攔截器:
注意 要聲明攔截器的攔截路徑窃诉,不然無法實(shí)現(xiàn)攔截功能
package com.lwq.controller;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* 攔截器配置
* Created by gaocl on 16-3-1.
*/
@Configuration
public class InterceptorConfigextends WebMvcConfigurerAdapter {
//注入需要攔截的類
? ?@Bean
? ? public ActionTestInterceptor logInterceptor() {
? ? ? ? ? ? ?return new ActionTestInterceptor();
? ? ?}
? ?@Override
? ? public void addInterceptors(InterceptorRegistry registry) {
? ? ? ? ? ? ? ? //配置攔截路徑,運(yùn)行訪問即可生效
? ? ? ? ? ? ? registry.addInterceptor(logInterceptor()).addPathPatterns("/**");
? ? ?}
}