spring AOP 和自定义注解进行身份验证

时间:2021-01-23 18:04:27

一个SSH的项目(springmvc+hibernate),需要提供接口给app使用。首先考虑的就是权限问题,app要遵循极简模式,部分内容无需验证,用过滤器不能解决某些无需验证的方法 所以最终选择用AOP 解决。大致思路是使用自定义注解,在需要权限控制的方法前(controller层)使用注解然后使用AOP拦截访问的方法,判断当前用户是否登录了(判断是否携带了登录之后获取到的 token ),从而决定是否拦截。

开启切面代理

    <!--aop配置,基于类的代理 -->
<!-- <aop:aspectj-autoproxy proxy-target-class="true"/>-->
<aop:aspectj-autoproxy/>

注意:1、一定要放在spring的配置文件中,不要单独新建一个文件

2、proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理将起作用。

编写一个自定义注解

@Retention(RetentionPolicy.RUNTIME)//注解会在class中存在,运行时可通过反射获取
@Target(ElementType.METHOD)//目标是方法
@Documented
public @interface LoginRequired{ }

ElementType.MeTHOD 表示该自定义注解可以用在方法上
RetentionPolicy.RUNTIME 表示该注解在代码运行时起作用

可以在自定义注解中加入一些默认方法

定义切面类验证权限

@Component
@Aspect
public class TokenInterceptor { private static final Logger logger = Logger.getLogger(TokenInterceptor.class); @Resource
private BllUserService bllUserService; @Pointcut("@annotation(org.jeecgframework.core.annotation.LoginRequired)")
public void serviceAspect() {
}
//环绕通知(特别适合做权限系统)
//@Before
@Around("serviceAspect()")
public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable{
AjaxJson json=new AjaxJson();
// String methodName = joinPoint.getSignature().getName();
// Object target = joinPoint.getTarget();
// Method method = getMethodByClassAndName(target.getClass(), methodName); //得到拦截的方法
Object[] args = joinPoint.getArgs();
HttpServletRequest request=(HttpServletRequest)args[0];
if(!validate(request)){
//request.setAttribute("message", "您没有执行该操作权限");
json.setMsg("您没有执行该操作权限");
json.setSuccess(false);
return json;
}
return joinPoint.proceed();
}
private boolean validate(HttpServletRequest request)throws Exception {

// String token=request.getParameter("token");//根据前端传值进行修改
String token=request.getHeader("token");
if(StringUtil.isEmpty(token)){ }
Map<String, Object> resultMap=Jwt.validToken(token);
TokenState state=TokenState.getTokenState((String)resultMap.get("state"));
switch (state) {
case VALID:
//取出payload中数据,放入到request作用域中
request.setAttribute("data", resultMap.get("data"));
break;
case EXPIRED://暂时没做
case INVALID:
return false;
}
return true; } public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
} public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}

@Aspect放在类头上,把这个类作为一个切面。

@Compenent注解标识其为Spring管理Bean,而@Aspect注解不能被Spring自动识别并注册为Bean,必须通过@Component注解来完成

注:这儿用了JWT做token验证,感兴趣的同学自行百度

TOKEN验证类

   /**
* 校验token是否合法,返回Map集合,集合中主要包含 state状态码 data鉴权成功后从token中提取的数据
* 该方法在过滤器中调用,每次请求API时都校验
* @param token
* @return Map<String, Object>
*/
public static Map<String, Object> validToken(String token) {
Map<String, Object> resultMap = new HashMap<String, Object>();
try {
JWSObject jwsObject = JWSObject.parse(token);
Payload payload = jwsObject.getPayload();
JWSVerifier verifier = new MACVerifier(SECRET); if (jwsObject.verify(verifier)) {
JSONObject jsonOBj = payload.toJSONObject();
// token校验成功(此时没有校验是否过期)
resultMap.put("state", TokenState.VALID.toString());
// 若payload包含ext字段,则校验是否过期
if (jsonOBj.containsKey("ext")) {
long extTime = Long.valueOf(jsonOBj.get("ext").toString());
long curTime = new Date().getTime();
// 过期了
if (curTime > extTime) {
resultMap.clear();
resultMap.put("state", TokenState.EXPIRED.toString());
}
}
resultMap.put("data", jsonOBj); } else {
// 校验失败
resultMap.put("state", TokenState.INVALID.toString());
} } catch (Exception e) {
//e.printStackTrace();
// token格式不合法导致的异常
resultMap.clear();
resultMap.put("state", TokenState.INVALID.toString());
}
return resultMap;
}

配置拦截器

    @RequestMapping(params = "physicalList")
@ResponseBody
@LoginRequired
public AjaxJson getPhysicalList(HttpServletRequest request){ }

测试

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
<title></title>
</head>
<body> <button type="button" onclick="getdata()">测试页面</button><br/>
<script type="text/javascript" src="js/jquery.min.js" ></script>
<script>
var token="123";
function getdata(){
$.ajax({
type:"post",
dataType:"json",
url:"",
headers:{
token:token//将token放到请求头中
},
// beforeSend: function(request) {
// request.setRequestHeader("token", token);
// },
success:function(data){
console.log(data);
$('body').append(JSON.stringify(data)); },
});
}
</script>
</body>
</html>

spring AOP 和自定义注解进行身份验证

可以发现页面请求被拦截了

参考:http://blog.csdn.net/caomiao2006/article/details/51287206

http://www.jianshu.com/p/576dbf44b2ae

http://www.scienjus.com/restful-token-authorization/