一. 思路
二. 具体实现
2.1 配置拦截器
2.2 具体拦截器的处理方法
2.3 注解的配置
2.4 权限的配置
2.5 注解的使用
2.6 总结
三. 提升(白名单,默认全都要有权限)
一. 思路
总体思路还是 利用 拦截器拦截每一个请求。
- 请求过来,拦截器拦截。
- 取得 该请求的方法,对应取得方法上(或者是类上)的权限注解。
- 再 通过 请求 传来的 用户Id 来得到用户被赋予的权限。
- 对比 方法上的权限注解 和 用户被赋予的权限。
- 匹配上 则让其通过,否则,不让其通过。
二.具体实现
2.1 配置 拦截器。
Spring Boot 配置拦截器可以继承 WebMvcAutoConfigurationAdaptor。如下代码:
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PrivilegeConfiguration extends WebMvcAutoConfigurationAdapter{
@Bean
public PrivilegeInterceptor createPrivilegeFilter(){
return new PrivilegeInterceptor();
}
@Override
public void addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry registry) {
registry.addInterceptor(createPrivilegeFilter());
};
}
2.2 具体的拦截器的处理方法:
@Slf4j
public class PrivilegeInterceptor implements HandlerInterceptor{
@Autowired
private StaffService staffService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
log.info("PrivilegeInterceptor class ... preHandle function ... ");
//获得类的权限注解 和方法的权限注解
HandlerMethod handlerMethod = (HandlerMethod) handler;
PrivilegeRequired privilegeRequiredClass = handlerMethod.getBeanType().getAnnotation(PrivilegeRequired.class);
PrivilegeRequired privilegeRequiredMethod = handlerMethod.getMethodAnnotation(PrivilegeRequired.class);
log.info("PrivilegeInterceptor class ... privilegeRequiredClass = {}.",privilegeRequiredClass);
log.info("PrivilegeInterceptor class ... privilegeRequiredMethod = {}.",privilegeRequiredMethod);
//如果没有,那么则允许通过
if(privilegeRequiredClass == null && privilegeRequiredMethod == null) return true;
//如果有,那么 先把权限收集,最后比较
Set<Privilege> priv = new HashSet<>();
if(privilegeRequiredClass != null){
priv.addAll(Arrays.asList(privilegeRequiredClass.value()));
}
if(privilegeRequiredMethod != null){
priv.addAll(Arrays.asList(privilegeRequiredMethod.value()));
}
Privilege[] privileges = priv.toArray(new Privilege[priv.size()]);
//对用户进行权限比较
String staffId = request.getParameter("staffId");
if(staffId == null || staffId.isEmpty()) return false;
Staff staff = staffService.findStaffById(staffId);
if(staff == null) return false;
Set<Role> granted = (Set<Role>) staff.getRoleList();
log.info("PrivilegeInterceptor class ... granted = {}.",granted);
for (Privilege privilege : privileges) {
for (Role role : granted) {
if (role.getRoleName().equals(privilege.getPrivilege())) {
log.info("PrivilegeInterceptor class ... granted pass. -> role.getRoleName() = {}. privilege.getPrivilege() = {}.",
role.getRoleName(),privilege.getPrivilege());
return true;
}
}
}
log.info("PrivilegeInterceptor class ... method not allowed.Beacuse of no authority.");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
Ps:这里的 二重 for 循环 可以改为 JDK1.8 的 并行流,提高效率。
2.3 注解的配置
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.METHOD})
public @interface PrivilegeRequired {
/**
* The name/code of the privileges required
*
* @return
*/
public Privilege[] value();
}
2.4 权限的配置
@ToString
public enum Privilege {
STAFF("STAFF"), //员工权限
ADMIN("ADMIN"); //管理员权限
private String privilege;
private Privilege(String privilege){this.privilege = privilege;}
public String getPrivilege(){
return this.privilege;
}
}
2.5 注解的使用:
在方法前(或者是类的前面)加入:
@PrivilegeRequired(Privilege.ADMIN)
2.6 总结:
拦截器的配置 和 权限之间的对比都不是特别的困难。就是 注意 如何取得类前面的权限 和方法前面的权限。使用利用了
HandlerMethod,该类的对象可以被 handler强转,然后就可以取得目前要访问的方法,从而也有方法获得权限相关的信息,也可以获得类上面的信息。
另外还有一个就是如何定义注解的问题。
三. 提升(白名单,默认全都要有权限)
有些程序员(包括本人)经常会忘记在方法前面写权限限制,这是不好的习惯,也和程序员自身水平有关。上面的Demo 的做法是 不写权限的注解,那么就默认没有权限,所有人都可以访问,这样其实不是特别好,因为实际上有些是需要权限的,只是程序员忘了。
所以另外一个解决办法可以是:默认所有的方法都不可以进入,需要权限才可以进入。如果实际上是真的不需要权限就可以进入,那么也要加一个 FreePrivilege 之类的注解。也就是说,没有权限也是权限的一种。