- 让我们认识注解
springboot实现权限控制可以使用shiro,是的springboot可以帮我们完成很多的事情,其实这篇文章的重点是注解,但是单独的说注解没什么意思,每样技术,我更关心的是它能做什么,我们为什么需要它,那么注解是我们需要的么?我想,它确实是被需要的,因为在自己写项目的时候会用到很多的注解比如说@RequestMapping、@Override,所以我觉的我需要了解一下它。
- 注解是什么?
我们先不说注解具体的定义是什么?我们先打个比方,说一下注解像什么,我的理解啊!注解像个便利贴,@Override是个便利贴,这个便利贴,提醒我在编译的时候检查是不是实现的是接口中的方法,有了这个注解,这个类本身的活动并没有发生改变,这个类是个处理servlet的类,加了注解后他还是这个功能,没有变,但是他影响了别人,这个别人可以看做是编译这件事情,编译的时候编译器看到了这个注解。
- 为什么我们需要注解?
有没有发现注解和一个东西很像,xml配置文件,我们在使用springMVC这个框架的时候,常常会用到这样的一个注解@RequestMapping,这个组件有一个value属性,用于记录路由信息,如果我们不适用springMVC这样的框架,如何实现一个servlet呢?可以参考下servlet 是什么 ,你会发现这里的这里的路由是通过XML来配置的,使用这种方式会有一些问题,比每次添加一个servlet都需要在XML文件当中配置一下,而如果使用注解,则只需要在controller处理方法的注解中添加一下,这里我们需要一个强耦合的配置方式。至于springMVC的@RequestMapping的实现方式可以参考下文档【7】。
- 实现基于注解的权限控制
创建权限注解
package com.deepwater.daisy.annocation;
import java.lang.annotation.*;
// 该注解作用于方法
@Target(ElementType.METHOD)
// 该注解在代码运行时也是存在的
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Access {
String[] value() default {};
String[] authorities() default {};
String[] roles() default {};
}
创建一个controller:
@RequestMapping(value = "/admin")
// 通过注解配置该handler只能被拥有admin权限的人调用
@Access(authorities = {"admin"})
public String hello() {
return "Hello, admin";
}
创建一个拦截器,用于在调用controller之前判断权限信息:
package com.deepwater.daisy.interceptor;
import com.deepwater.daisy.annocation.Access;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor ;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.*;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
/**
* @author jcxu
* @date 20171012
* @description 定义了一个拦截器
*/
public class UserInterceptor implements HandlerInterceptor {
public UserInterceptor() {
super();
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("请求开始被执行!");
// 将handler强转为HandlerMethod, 前面已经证实这个handler就是HandlerMethod
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 从方法处理器中获取出要调用的方法
Method method = handlerMethod.getMethod();
// 获取出方法上的Access注解
Access access = method.getAnnotation(Access.class);
if (access == null) {
// 如果注解为null, 说明不需要拦截, 直接放过
return true;
}
if (access.authorities().length > 0) {
// 如果权限配置不为空, 则取出配置值
String[] authorities = access.authorities();
Set<String> authSet = new HashSet<>();
for (String authority : authorities) {
// 将权限加入一个set集合中
authSet.add(authority);
}
// 这里我为了方便是直接参数传入权限, 在实际操作中应该是从参数中获取用户Id
// 到数据库权限表中查询用户拥有的权限集合, 与set集合中的权限进行对比完成权限校验
String role = request.getParameter("role");
if (!role.isEmpty()) {
if (authSet.contains(role)) {
// 校验通过返回true, 否则拦截请求
return true;
}
}
}
// 拦截之后应该返回公共结果, 这里没做处理
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("请求正在被执行!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("请求被执行完成!");
}
}
注册该拦截器:
package com.deepwater.daisy.configuration;
import com.deepwater.daisy.interceptor.UserInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//这里会对"/admin"的请求进行拦截
registry.addInterceptor(new UserInterceptor()).addPathPatterns("/admin");
}
}
我们可以看到没有请求会交个一个handler来处理,这个handler是谁呢!他就是我们用@RequestMapping注解的方法啊!我们会通过反射,找到注解对象的属性,获取到的属性,就像是获取到了该handler的标签,我们知道了这些handler的权限信息,注解对于其作用的包、类、方法并没有产生影响,但是在拦截器中,通过判断注解信息,实现了不同的操作。