在工作项目实战开发中,用户请求API接口的时候我们会用到一个叫做权限控制的东西,我们并不希望所有用户都能访问某个接口或者网页。这个时候需要做权限控制,指定用户的角色,看这个角色是否有访问权限,然后决定其是否有访问接口网站的权限。
网上有很多这种解决方案,用于这种场景,这些方案都离不开以下技术选型:
- 自定义注解
- AOP
本文要解决的就是,用一个简单优雅的案例讲解这种方案,并且会在方案中加入一个控制权限功能的开关!改进这种方案!本文是我在工作项目中遇到的一种情况,这个功能开发完了,所以打算分享一下自己改进这种技术方案的做法,欢迎大家指正!
自定义注解
自定义注解的目的就是是用我们自己的注解去约束你是否有访问某个函数接口的权限,比如下面自己定义的注解 HasRole
。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface HasRole { String key(); }
当我在某个函数API上面放上注解,这个函数的访问就被我们加了权限。
AOP面向切面编程
谈到AOP,我曾经写过一篇博客总结过关于AOP的知识。大家可以看我的这篇博客 《AOP面向切面编程》
我们会将一些业务放到切面类的前置通知里,当用户访问服务的时候,会先执行前置通知里的业务,这个业务其实就是一个用户身份的校验判断与提示。
比如,当我们在接口请求测试的时候会出现如下响应:
这个判断与提示其实就是在前置通知里完成的。
在AOP里,我的业务逻辑是这么写的:
/** * @Description: * @return: * @Author: codelinghu * @Date: 2024/3/27 */ @Component @Aspect @ConditionalOnProperty(prefix = "permission_switch", name = "enable", havingValue = "true") public class AopConfig { //定义一个切点(通过注解) @Pointcut("@annotation(com.tongda.tdjobcard.config.annotation.HasPermission))") public void pointcut(){} private final Map<String, BaseXClient> connectMap; public AopConfig(Map<String, BaseXClient> connectMap) { this.connectMap = connectMap; } //前置通知,判断权限 @Before("pointcut()") public void before(JoinPoint joinPoint){ //获取到HttpServletRequest,ThreadLocal RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes; HttpServletRequest request = servletRequestAttributes.getRequest(); //获取用户请求的userID String userid = request.getHeader("userID"); //获取当前请求的方法上的注解HasPermission中设置的角色 MethodSignature signature = (MethodSignature)joinPoint.getSignature(); // System.out.println("key="+signature.getName()); BaseXClient client = connectMap.get("adminUser"); if (client == null) { throw new BusinessException(ResultVoEnum.ACCOUNT_ERROR, null); } String s= PermissionDao.hasPermission(client, TdCalcConfig.DEFAULT_DBNAME,userid,signature.getName()); //开始判断true or false // true放行,false拦住抛出异常 if (!"true".equals(s)){ throw new BusinessException(ResultVoEnum.NO_PERMISSION, null); } } } }
其实到这里,我的功能已经实现了,接下来就是加一个配置文件的开关就行了~
@ConditionalOnProperty配置文件开关
说一下这个@ConditionalOnProperty注解哈,这个注解就是开启上面自定义注解的一个开关,在配置文件里进行开启或关闭。
如果我设置enable为false,那么我的自定义注解就会失效,下面的代码就不会被执行:
@ConditionalOnProperty(prefix = "permission_switch", name = "enable", havingValue = "true")
参考文献
- 《Spring 自定义注解从入门到精通》
- 《【SpringBoot框架篇】20.自定义@Configuration配置类启用开关》
- 《【SpringBoot框架篇】20.自定义@Configuration配置类启用开关》