springboot + 拦截器 + 注解 实现自定义权限验证
最近用到一种前端模板技术:jtwig,在权限控制上没有用springSecurity。因此用拦截器和注解结合实现了权限控制。
1.1 定义权限常量 PermissionConstants.java
public class PermissionConstants { /** * 管理员-产品列表查询 */ public static final String ADMIN_PRODUCT_LIST = "admin_product_list"; /** * 管理员-产品详情 */ public static final String ADMIN_PRODUCT_DETAIL = "admin_product_detail"; }
- 权限也可以不定义为常量,看项目情况
1.2 定义权限的注解 RequiredPermission.java
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface RequiredPermission { String value(); }
- ElementType.TYPE,ElementType.METHOD表示注解可以标记类和方法
1.3 权限拦截器 SecurityInterceptor.java
public class SecurityInterceptor implements HandlerInterceptor { @Autowired private AdminUserService adminUserService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 验证权限 if (this.hasPermission(handler)) { return true; } // null == request.getHeader("x-requested-with") TODO 暂时用这个来判断是否为ajax请求 // 如果没有权限 则抛403异常 springboot会处理,跳转到 /error/403 页面 response.sendError(HttpStatus.FORBIDDEN.value(), "无权限"); return false; } /** * 是否有权限 * * @param handler * @return */ private boolean hasPermission(Object handler) { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; // 获取方法上的注解 RequiredPermission requiredPermission = handlerMethod.getMethod().getAnnotation(RequiredPermission.class); // 如果方法上的注解为空 则获取类的注解 if (requiredPermission == null) { requiredPermission = handlerMethod.getMethod().getDeclaringClass().getAnnotation(RequiredPermission.class); } // 如果标记了注解,则判断权限 if (requiredPermission != null && StringUtils.isNotBlank(requiredPermission.value())) { // redis或数据库 中获取该用户的权限信息 并判断是否有权限 Set<String> permissionSet = adminUserService.getPermissionSet(); if (CollectionUtils.isEmpty(permissionSet) ){ return false; } return permissionSet.contains(requiredPermission.value()); } } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO } }
1.4 拦截器注入的配置 MVCConfig.java
@Configuration public class MVCConfig extends WebMvcConfigurerAdapter { @Bean public SecurityInterceptor securityInterceptor() { return new SecurityInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(securityInterceptor()).excludePathPatterns("/static/*") .excludePathPatterns("/error").addPathPatterns("/**"); } }
- springboot中注入拦截器
1.5 ProductController.java
@Controller @RequestMapping("/product") // @PermissionConstants.ADMIN_PRODUCT_MANAGEMENT public class ProductController { /** * 产品列表 * * @return */ @RequestMapping("/list") @RequiredPermission(PermissionConstants.ADMIN_PRODUCT_LIST) // 权限注解 public String list() { // 省略产品列表查询逻辑 return "/product/list"; } /** * 产品详情 * * @return */ @RequestMapping("/detail") @RequiredPermission(PermissionConstants.ADMIN_PRODUCT_DETAIL) // 权限注解 public String detail() { // 省略查询产品详情的逻辑 return "/product/edit"; } /** * 删除产品 * * @return */ @RequestMapping("/delete") public String delete() { // 省略删除产品的逻辑 return "/product/list"; } }
- 如果没有标记权限注解,则不会验证该请求的权限,如/product/delete 请求