项目中需要对部分接口进行是否登录验证
主要通过自定义注解,使用SpringAOP的环绕通知拦截请求,判断该方法是否有自定义注解,然后判断该用户是否有该权限。这里做的比较简单,只有两个权限:一个普通用户、一个管理员。
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
相关注解
@Before 前置通知,在方法执行之前执行
@After 后置通知,在方法执行之后执行
@AfterRuturning 返回通知,在方法返回结果之后执行
AfterThrowing 异常通知,在方法抛出异常之后执行
Around 环绕通知,围绕着方法执行
PointCut 表达式详解
execution:方法的返回值类型 包路径(可省略) 方法的名称(参数) 异常类型(可省略) )
-
方法类型包含Public,Protected等,可省略
-
方法返回值类型,*可以包含所有的返回值类型
-
包路径,如“com.demo…*”,表示"com.demo"包以及该包之下子包的所有类型
-
方法名称,如“add”,表示所有以add开头的方法,参数:()表示任意一个参数,(…)表示所有参数
-
异常类型,如execution(* *(…) throws Exception)”匹配所有抛出Exception的方法
within:是用来指定类型的,指定类型中的所有方法将被拦截是用来指定类型的,指定类型中的所有方法将被拦截
-
within(com.demo.service.impl.UserServiceImpl) 匹配UserServiceImpl类对应对象的所有方法调用,并且只能是UserServiceImpl对象,不能是它的子对象
-
within(com.demo…*)匹配com.demo包及其子包下面的所有类的所有方法的外部调用。
this:SpringAOP是基于代理的,this就代表代理对象,语法是this(type),当生成的代理对象可以转化为type指定的类型时表示匹配
- this(com.demo.service.IUserService)匹配生成的代理对象是IUserService类型的所有方法的外部调用
target:SpringAOP是基于代理的,target表示被代理的目标对象,当被代理的目标对象可以转换为指定的类型时则表示匹配。
- target(com.demo.service.IUserService) 匹配所有被代理的目标对象能够转化成IuserService类型的所有方法的外部调用。
args:args用来匹配方法参数
- args() 匹配不带参数的方法
- args(java.lang.String) 匹配方法参数是String类型的
- args(…) 带任意参数的方法
- args(java.lang.String,…) 匹配第一个参数是String类型的,其他参数任意。最后一个参数是String的同理。
@within 和 @target:带有相应标注的所有类的任意方法,比如@Transactional
@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)
@annotation:带有相应标注的任意方法,比如@Transactional
@annotation(org.springframework.transaction.annotation.Transactional)
- @within和@target针对类的注解,@annotation针对方法的注解
@args:参数带有相应标注的任意方法,比如@Transactiona
@args(org.springframework.transaction.annotation.Transactional)
PointCut使用
/**
* 拦截controller下的所有方法
*/
@Pointcut("execution(public * com.mmall.controller..*.*(..))")
public void loginRequired() {
}
自定义注解及解析
- 因为分客户端和管理端 定义不同注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LoginRequired {
/**
* 是否需要登录
* @return
*/
boolean loginRequired() default true;
}
- 管理端
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AdminLoginRequired {
/**
* 是否需要登录
* @return
*/
boolean loginRequired() default true;
}
- 解析类:通过AOP的环绕通知获取方法上的注解,判断是否有Permission注解,返回注解的值。
/**
* 判断一个方法普通用户是否需要登录
*
* @param method 方法
* @return
*/
private boolean isLoginRequired(Method method) {
boolean result = false;
if (method.isAnnotationPresent(LoginRequired.class)) {
result = method.getAnnotation(LoginRequired.class).loginRequired();
}
return result;
}
/**
* 判断一个方法管理员用户是否需要登录
*
* @param method 方法
* @return
*/
private boolean adminIsLoginRequired(Method method) {
boolean result = false;
if (method.isAnnotationPresent(AdminLoginRequired.class)) {
result = method.getAnnotation(AdminLoginRequired.class).loginRequired();
}
return result;
}
- 判断用户是否登录
private Map<String, Object> isLogin(HttpServletRequest request) {
String token = request.getHeader("token");
//默认没登录普通管理员
Map<String, Object> map = new HashMap<String, Object>();
map.put("role", 0);
map.put("isLogin", false);
if (null == token || "" == token) {
return map;
}
String res = redisUtil.get(token) + "";
User user = JSONObject.parseObject(res, User.class);
if (user != null) {
map.put("isLogin", true);
if (user.getRole() == 1) {
map.put("role", 1);
}
}
return map;
}
SpringAOP环绕通知
/**
* 拦截器具体实现
*
* @param
* @return
*/
@Around("loginRequired()") //指定拦截器规则;
@ResponseBody
public Object Interceptor(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod(); //获取被拦截的方法
String methodName = method.getName(); //获取被拦截的方法名
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
//判断该方法上没有权限注解,没用直接调用目标方法
if (isLoginRequired(method) == true || adminIsLoginRequired(method) == true) {
boolean loginResult = (boolean) isLogin(request).get("isLogin");
if (loginResult == false) {
return ServerResponse.createByErrorCodeMessage(100, "用户未登录");
}else {
if ((Integer) isLogin(request).get("role") == 0 && adminIsLoginRequired(method) == true) {
return ServerResponse.createByErrorCodeMessage(100, "当前用户不是管理员");
}
}
}
return pjp.proceed();
}
使用
- 普通用户添加LoginRequired注解
@PostMapping("/updateInfo")
@LoginRequired
public ServerResponse<String> updateInfo(@RequestBody User user, HttpSession session) throws MmallException {
ServerResponse response = userService.updateInfo(user);
//更新成功刷新缓存
if (response.isSuccess()) {
session.setAttribute(Const.CURRENT_USER, response.getData());
} else {
return ServerResponse.createBySuccessMessage("更新失败");
}
return ServerResponse.createBySuccess();
}
- 管理员添加AdminLoginRequired
@PostMapping("/categorySave")
@AdminLoginRequired
public ServerResponse<String> categorySave(@RequestBody Category category) {
return categoryService.categorySave(category);
}
网友评论