介绍: 权限管理是一个系统中及其重要的部分,用户都有各自的角色,但是可以做的事情是不一样的,我们在每一个可以执行的方法中做判断是很费力并别维护起来也是十分复杂的。
自定义权限注解,玩转权限控制
(1)定义注解权限
1、 定义一个AuthAnnotation注解类,用@interface表示是一个注解类
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthAnnotation {
Auth[] value(); //权限key
}
(1)@Target 定义注解的使用位置 。
ElementType.TYPE
: 说明该注解只能被声明在一个类前。
ElementType.FIELD
: 说明该注解只能被声明在一个类的字段前。
ElementType.METHOD
: 说明该注解只能被声明在一个类的方法前。
ElementType.PARAMETER
: 说明该注解只能被声明在一个方法参数前。
ElementType.CONSTRUCTOR
: 说明该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE
: 说明该注解只能声明在一个局部变量前。
ElementType.ANNOTATION_TYPE
: 说明该注解只能声明在一个注解类型前。
ElementType.PACKAGE
: 说明该注解只能声明在一个包名前
(2)@Retention 用来说明该注解类的生命周期。
RetentionPolicy.SOURCE
: 注解只保留在源文件中
RetentionPolicy.CLASS
: 注解保留在class文件中,在加载到JVM虚拟机时丢弃
RetentionPolicy.RUNTIME
: 注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解。
(3)@Documented 是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。
(4) @Inherited 指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。 对那些@Target被定义为ElementType.TYPE的自定义注解起作用。
2、Auth 是我们的枚举类,对映我们的所有角色权限
public enum Auth {
ADMIN("超级管理员"),
COMMON("普通成员");
private String name;
private Auth(String name){
this.name=name;
}
public String getName(){
return name;
}
}
(2)定义权限拦截器
@Component
public class authInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod= (HandlerMethod) handler; //拿到我们的controller信息
AuthAnnotation authAnnotation = fingAnnotation(handlerMethod); //判断是否存在注解
if(!Arrays.asList(authAnnotation.value()).contains(Auth.ADMIN)){
//Auth.Admin用户角色应该用cokie中或者session中获取
setCorsMapping(request,response,"权限不足");
return false;
}
return true;
}
private AuthAnnotation fingAnnotation(HandlerMethod handlerMethod) {
//先查找方法上寻找注解
AuthAnnotation methodAnnotation = handlerMethod.getMethodAnnotation(AuthAnnotation.class);
if(methodAnnotation==null){
//在类上找到
methodAnnotation = handlerMethod.getBeanType().getAnnotation(AuthAnnotation.class);
}
return methodAnnotation;
}
/**
* 跨域返回,权限不住时,返回数据
* @param request
* @param response
* @param message
*/
private void setCorsMapping( HttpServletRequest request,HttpServletResponse response,String message){
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setContentType("application/json;charset=utf-8");
response.setStatus(403);
try{
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(message));
writer.flush();
writer.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
我们先查找controller的类方法上是否存在注解,如果有就按照方法上的定义,需要什么角色访问,如果方法不存在的话查找。
handlerMethod.getBeanType().getAnnotation(AuthAnnotation.class);
我们在类上查找注解,查看什么角色可以访问。
配置拦截器加载:
@Component
public class myMvcConfig extends WebMvcConfigurationSupport {
@Autowired
private authInterceptor authInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor).addPathPatterns("/**"); //添加拦截器,拦截所有请求
}
@Override
protected void addCorsMappings(CorsRegistry registry) { //配置跨域
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "HEAD", "POST","PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("access-control-allow-headers",
"access-control-allow-methods",
"access-control-allow-origin",
"access-control-max-age",
"X-Frame-Options")
.allowCredentials(false).maxAge(3600);
super.addCorsMappings(registry);
}
@Override //支持fastjson
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
for (int i = converters.size() - 1; i >= 0; i--) {
if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
converters.remove(i);
}
}
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
//自定义fastjson配置
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(
SerializerFeature.WriteMapNullValue, // 是否输出值为null的字段,默认为false,我们将它打开
SerializerFeature.WriteNullListAsEmpty, // 将Collection类型字段的字段空值输出为[]
SerializerFeature.WriteNullStringAsEmpty, // 将字符串类型字段的空值输出为空字符串
SerializerFeature.WriteNullNumberAsZero, // 将数值类型字段的空值输出为0
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.DisableCircularReferenceDetect // 禁用循环引用
);
fastJsonHttpMessageConverter.setFastJsonConfig(config);
// 添加支持的MediaTypes;不添加时默认为*/*,也就是默认支持全部
// 但是MappingJackson2HttpMessageConverter里面支持的MediaTypes为application/json
// 参考它的做法, fastjson也只添加application/json的MediaTyp
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
converters.add(fastJsonHttpMessageConverter);
}
}
在我们的controller层添加注解,就可以测试了!