背景描述
通过使用自定义注解和拦截器的方式,来拦截请求验证用户权限。
- 自定义注解:
@UserAuth
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface UserAuth {
String[] value() default {};
}
- 拦截器:
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler instanceof HandlerMethod){
UserAuth userAuth = ((HandlerMethod) handler).getMethodAnnotation(UserAuth.class);
if(userAuth != null){
//权限验证
}
}
return true;
}
}
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
private final MyInterceptor myInterceptor;
public InterceptorConfig(MyInterceptor myInterceptor) {
this.myInterceptor = myInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor).addPathPatterns("/*");
}
}
@UserAuth
中的value
属性值想通过properties或yml
配置文件动态注入。
- application.properties
auth.key=search_list
- Controller
@RestController
public class DemoController {
@RequestMapping("/")
@UserAuth("${auth.key}")
public String demo(){
return "hello world!" ;
}
}
- 使用
ConfigurableEnvironment
类用来解析"${auth.key}"
字符串。对拦截器进行改造:
@Component
public class MyInterceptor implements HandlerInterceptor {
@Autowired
private ConfigurableEnvironment environment;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler instanceof HandlerMethod){
//通过反射得到自定义注解
UserAuth userAuth = ((HandlerMethod) handler).getMethodAnnotation(UserAuth.class);
if(userAuth != null && userAuth.value().length > 0){
//得到配置文件中对应的值
String codes = Arrays.stream(userAuth.value())
.map(key -> environment.resolvePlaceholders(key))
.collect(Collectors.joining(","));
System.out.println(codes);
}
}
return true;
}
}
首先通过反射得到自定义注解@UserAuth
的value
值"${auth.key}"
,然后以此作为key,通过environment.resolvePlaceholders()
方法得到配置文件中对应的值。也可以把@UserAuth
的value
值设为"auth.key"
,然后通过environment.getProperty()
方法得到对应的值。
以上,已经实现了功能。但在得到这个方案的过程中,发现了更深层次的知识点,下面详细记录下。
知识点
-
使用
@ConfigurationProperties
注解把application.properties
的值注入到对象里@Component @ConfigurationProperties(prefix="auth") public class MyProperties{ private String key; //set,get... }
Controller改造
@RestController public class DemoController { @Autowired private MyProperties properties; @RequestMapping("/") @UserAuth(properties.getKey()) public String demo(){ return "hello world!" ; } }
@UserAuth(properties.getKey())
报错Attribute value must be constant
,注解里的属性值必须为常量,即static final
修饰。
发现@ConfigurationProperties
注解可以注入静态属性,参考:https://www.jianshu.com/p/149c5a951ffc
使用静态还是同样的错,
@UserAuth(MyProperties.key)
-
查找注解的原理,发现了可以通过反射来动态修改注解属性的值,可参考:
https://blog.csdn.net/u014750606/article/details/79977114
https://www.cnblogs.com/acm-bingzi/p/javaAnnotation.html其中
memberValues 的来源是Java 常量池
。