springboot 注解+拦截器 实现角色权限拦截

3,645 阅读4分钟

第一步:

需要注解实现权限拦截,先创建自定义注解类:

import java.lang.annotation.*;

/**
 * 角色注解
 * 
 */
@Target({ElementType.METHOD})								// 方法注解
@Retention(RetentionPolicy.RUNTIME)							// 运行时
//@Documented
public @interface Role{
    // 默认
    String value() default "user";

    // 拥有其中一个角色就通过
    String[] roles() default {};

    // 必须这些角色才通过
    String[] rolesMust() default {};
}

第二步:

然后就自定义一个拦截器类(继承HandlerInterceptorAdapter就行了):

为了简单理解,我这里从前端发送一个 userId 来模拟登录,如果有说明登录了,没有就没登录。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 自定义一个权限拦截器, 继承HandlerInterceptorAdapter类
 */
@Component        // 交给spring管理
public class RoleInterceptor extends HandlerInterceptorAdapter {

    // 在调用方法之前执行拦截
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        System.out.println("经过了RoleInterceptor拦截器");

        // 将handler强转为HandlerMethod, 前面已经证实这个handler就是HandlerMethod
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        // 从方法处理器中获取出要调用的方法
        Method method = handlerMethod.getMethod();

        // 获取出方法上的Role注解
        Role role = method.getAnnotation(Role.class);
        // 如果注解为null, 说明不需要拦截, 直接放过
        if (role == null) {
            return true;
        }
        
        // 从前端直接发一个userId来模拟登录,如果有说明登录了,没有就没登录
        String userId = (String) request.getParameter("userId");
        // 没有信息说明没有登陆
        if (userId == null || userId.length()==0) {
            //自定义登录异常
            throw new LoginException();
        }

        // 到数据库权限表中查询用户拥有的角色集合, 进行对比完成权限校验
        List<String> userRole = 到数据库查;

        if (userRole != null && userRole.size()>0) {
            // 写了注解说明需要权限
            // 拥有其中一个角色就通过
            if (role.roles().length > 0) {
                // 获取多个角色
                // 如果权限配置不为空, 则取出配置值
                String[] roles = role.roles();

                // 校验角色权限操作,自己实现.....
                // 如果角色校验不通过,在这可以不处理,留到最后处理
                
            } else if (role.rolesMust().length > 0) {

                // 必须有这些角色才通过
                String[] rolesMust = role.rolesMust();

                // 校验角色权限操作,自己实现.....
                // 如果角色校验不通过,在这可以不处理,留到最后处理

            } else {
                // 默认user
                String value = role.value();
                for (String str : userRole) {
                    if (value.equals(str)) return true;
                }
            }
        }
        
        // 说明没登录抛出自定义的角色异常,没有自定义异常可以直接返回false,代码就不会执行下去
        throw new RoleException();

        // 拦截之后应该返回公共结果, 这里没做处理
        // return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//        System.out.println("postHandler");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//        System.out.println("afterCompletion");
    }

}

说明: 拦截器返回的只能是boolean类型,如果想返回其他值是不行的。但是一般我们前端都要有简单清晰友好的信息

像这样的:

{
    “code”:“ok”,
    “data”:“成功”
}

像这种json数据,拦截器就不能返回。所以我们可以自定义异常,这样在抛出异常后,再用全局异常类来处理异常的时候就可以返回我们所需要的数据。

​创建自定义异常 LoginException:

package com.xoppen.user.exception;
​
import com.xoppen.common.entity.StatusMessage;
​
/**
 * 登录异常
 * @Author: Xu
 * @Date: 2019/7/9 10:12
 */
public class LoginException extends Exception{
    public LoginException(Exception e) {
        super(e);
    }
    public LoginException(String msg, Exception e) {
        super(msg, e);
    }
    public LoginException(String msg) {
        super(msg);
    }
    public LoginException(){
        super(StatusMessage.ERROR_LOGIN);
    }
}

​创建全局异常处理类 BaseExceptionHandler :

package com.xoppen.user.exception;
​
import com.xoppen.common.entity.Result;
import com.xoppen.common.entity.ResultUtil;
import com.xoppen.common.entity.StatusCode;
import com.xoppen.common.entity.StatusMessage;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
 * 统一异常处理类
 */
@ControllerAdvice
public class BaseExceptionHandler {
  
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        // ResultUtil 这是返回状态码和提示信息的工具类
        
        //分别处理具体异常
        // 业务异常直接反馈给用户
        if (e instanceof LoginException) {
            //登陆异常
            return ResultUtil.error(StatusCode.ERROR_LOGIN, e.getMessage());
        }else if (e instanceof RoleException){
            // 角色异常
            return ResultUtil.error(StatusCode.ERROR_ACCESS, e.getMessage());
        }else if (e instanceof HttpRequestMethodNotSupportedException) {
            // 请求方式异常
            return ResultUtil.error(StatusCode.ERROR, StatusMessage.ERROR_REQUEST);
        }else if (e instanceof DataAccessResourceFailureException){
            // 数据源连接超时
            return ResultUtil.error(StatusCode.ERROR, StatusMessage.ERROR_DATABASE);
        } else if (e instanceof ErrorException){
            return ResultUtil.error(StatusCode.ERROR, StatusMessage.ERROR);
        }
​
        // 未知异常返回统一报错信息
        return ResultUtil.error( StatusCode.ERROR_SYSTEM, e.getLocalizedMessage());
    }
}
​

这样就可以返回我们所需要的信息了;

第三步:

配置拦截器的配置类(继承WebMvcConfigurationSupport ),让拦截器生效:

import com.xoppen.user.interceptor.JwtInterceptor;
import com.xoppen.user.interceptor.RoleInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * 配置拦截器的拦截路径
 */
@Configuration                  //表明是个配置类
public class InterceptorConfig extends WebMvcConfigurationSupport {

    @Autowired
    private RoleInterceptor roleInterceptor;

    protected void addInterceptors(InterceptorRegistry registry) {
        //注册拦截器要声明拦截器对象和要拦截的请求

        // 角色拦截器
        registry.addInterceptor(roleInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/**/login/**","/**/register/**");
      
        //addInterceptor(roleInterceptor)     配置拦截器
        //addPathPatterns("/**")              拦截器需要拦截哪些路径
        //excludePathPatterns("/**/login/**","/**/register/**")   拦截器不需要拦截哪些路径
        
    }
}

第四步:

在controller上的方法直接写注解就行了:

像我自定义的角色注解 有多种写法, 写法有很多。主要的是你在校验角色的时候怎么处理(重要)

import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * 用户Controller
 */
@RestController                                                //将返回的数据转成Json数据
@CrossOrigin                                                   //解决跨域问题
@RequestMapping("/user")                                       //添加前缀
public class UserController {
    
    // 像我自定义的角色注解有多种写法,写法有很多。主要的是你在校验角色的时候怎么处理
    // 注解第一种
    // 这种就一个单单注解,没有参数
    @Role
    @RequestMapping(value = "/name")
    public Result name1(){
         System.out.println("通过啦!!!!!!");
    }

    // 第二种
    @Role(roles = {"user","admin"})
    @RequestMapping(value = "/name")
    public Result name2(){
         System.out.println("通过啦!!!!!!");
    }
    
    // 第三种
    @Role(rolesMust = {"user","admin"})
    @RequestMapping(value = "/name")
    public Result name3(){
         System.out.println("通过啦!!!!!!");
    }

    // 第四种
    @Role(roles = {"user","admin"},rolesMust = {"user","admin"})
    @RequestMapping(value = "/name")
    public Result name4(){
         System.out.println("通过啦!!!!!!");
    }

}

这样就配置完成啦!!!!!

----------------------------------------------------------完毕---------------------------------------------------------