Springboot整合通过aop实现权限拦截过滤
学习理论知识,概念需要理清:
- Pointcut:切点,决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为execution方式和annotation方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
- Advice:处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
- Aspect:切面,即Pointcut和Advice。
- Joint point:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
- Weaving:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。
参阅图片,可以梳理:
Pointcut切入点,归纳定义相应的join point;
织入器(参照pointcut定义取得相应的join point)将对应此pointcut的advice,织入到pointcut指定的join point初;
Join point执行具体的业务操作。
扩展 :
代码实践
- 定义注释:
/**
* @Target 此注解的作用目标,括号里METHOD的意思说明此注解只能加在方法上面
* @Retention 注解的保留位置,括号里RUNTIME的意思说明注解可以存在于运行时,可以用于反射
* @Documented 说明该注解将包含在javadoc中
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StaffAnnotation {
}
- 定义切面
/**
* 此类为一个切面类,主要作用就是对接口的请求进行拦截
* 拦截的方式,只需要在指定接口方法上面加上@StaffAnnotation注解即可
*/
@Aspect
@Component
public class AuthorityAspect {
@Autowired
IUserService ius;
//使用org.slf4j.Logger,这是spring实现日志的方法
private final static Logger logger = LoggerFactory.getLogger(AuthorityAspect.class);
/**
* @Pointcut 注解指定一个切面,定义需要拦截的东西
* 表示在执行被@MonitorRequest注解修饰的方法之前 会执行doBefore()方法
*/
@Pointcut(value = "@annotation(com.sjy.it.user.annotation.StaffAnnotation)")
public void checkStaffAuthority() {
}
/**
*
* @param joinPoint 连接点,就是被拦截点
*/
@Before("checkStaffAuthority()")
public void checkStaff(JoinPoint joinPoint) {
//获取到请求的属性
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//获取到请求对象
HttpServletRequest request = attributes.getRequest();
//URL:根据请求对象拿到访问的地址
logger.info("url=" + request.getRequestURL());
//获取请求的方法,是Get还是Post请求
logger.info("method=" + request.getMethod());
//ip:获取到访问
logger.info("ip=" + request.getRemoteAddr());
//获取被拦截的类名和方法名
logger.info("class=" + joinPoint.getSignature().getDeclaringTypeName() +
"and method name=" + joinPoint.getSignature().getName());
//参数
logger.info("参数userId:" + Arrays.toString(joinPoint.getArgs()));
Object[] joinPointArgs = joinPoint.getArgs();
if(joinPointArgs==null){
throw new ParameterException("用户信息拉取异常");
}
int userId = (int)joinPointArgs[0];
logger.info("解析userId:"+userId);
User user = ius.findUserById(userId);
if(user==null){
throw new AccountNotFoundException("账户不存在");
}
boolean hasPrivs = CheckPrivsCacheUtil.checkPrivs(user, PrivsEnmus.STAFF.getCode());
if(hasPrivs){
logger.info("鉴权成功,user:"+user.getUsername()+",权限:"+PrivsEnmus.STAFF.getName());
} else {
logger.error("鉴权失败,user:"+user.getUsername()+",权限:"+PrivsEnmus.STAFF.getName());
throw new AuthorityException("权限不足");
}
}
}
- 定义Controller接口
@RequestMapping("/getStaffList")
@StaffAnnotation
public HttpResp getStaffList(@RequestParam(value = "userId") Integer userId) {
List<User> staffList = ius.findAll();
if (staffList != null && !staffList.isEmpty()) {
return new HttpResp(0, "查询员工列表成功", staffList, new Date());
} else {
return new HttpResp(-1, "未查询到,请重试!", null, new Date());
}
}
- 发起请求:
getStaffList(){
var _this = this;
for (var state in _this.states) {
_this.states[state] = 'display:none';
}
_this.states.showStaffList = 'display:block';
axios.get('../api/staff/getStaffList?userId='+_this.userId)
.then(function (response) {
console.log(response.data);
if (response.data.code == 0) {
_this.staffTable = response.data.results;
} else {
if(response.data.msg!=null && response.data.msg!=undefined){
alert(response.data.msg);
} else {
alert("拉取数据失败,请重试!")
}
_this.backToIndex();
}
})
.catch(function (error) {
console.log(error);
_this.backToIndex();
});
- 运行结果: