SpringMVC自定义注解实现权限验证

自定义注解实现权限验证

第一步,定义注解——相当于定义标记;
  • @Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的。以下是ElementType全部的作用域,可根据项目需求进行选择。

/** 类,接口(包括注解类型)或枚举的声明 /
TYPE,
/
* 属性的声明 /
FIELD,
/
* 方法的声明 /
METHOD,
/
* 方法形式参数声明 /
PARAMETER,
/
* 构造方法的声明 /
CONSTRUCTOR,
/
* 局部变量声明 /
LOCAL_VARIABLE,
/
* 注解类型声明 /
ANNOTATION_TYPE,
/
* 包的声明 */
PACKAGE

  • @Retention注解用来修饰自定义注解的生命力。注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。
/**
* Annotations are to be discarded by the compiler.
* (注解将被编译器忽略掉)
*/
SOURCE,

/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time.  This is the default
* behavior.
* (注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为)
*/
CLASS,

/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
* (注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到)
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME,
  • @Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。

  • @Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。@Inherited注解只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。

通过以上这些元注解,博主这里写了一个自定义注解

//类和方法上生效
@Target({ElementType.METHOD,ElementType.TYPE})
//运行时有效
@Retention(RetentionPolicy.RUNTIME)
//在javadoc文档中使用(这个不重要)
@Documented
public @interface XXSecurity {
    long [] value() default {};
}

拦截器的配置在我的博文Interceptor与Shiro小结有说明,这里不再重复介绍

第二步,配置注解——把标记打在需要用到的程序代码中

博主将该自定义注解配置到拦截器中,实现一个通过注解进行用户权限验证的功能。

这里用三个test方法来进行自定义注解权限的验证。

注解使用最常见的方法**@自定义注解名(这个注解中的属性名=需要设置的值)**

@Controller
@RequestMapping("/user")
@ResponseBody
public class UserController {

    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    @Resource
    private UserDAO userDAO;

    // 进行加密,运行程序时,可直接返回userId来代替加密环节
    @GetMapping(value = "/test1")
    public String test1(Long userId){
        return JWTUtil.sign(userId,86400000);
        // return String.valueOf(userid);
    }

    //这个方法只能有RoleConstant.One和RoleConstant.Two的人可一访问
    @RequestMapping(value = "test2", method = RequestMethod.GET)
    @XXSecurity({RoleConstant.One,RoleConstant.Two})
    public String test2(@RequestHeader("token")String token) {
        Long userId = JWTUtil.unsign(token,Long.class);
        UserEntity userEntity = userService.getUserById(userId);
        return "OK";
    }

    //这个方法只能有RoleConstant.Four的人可一访问
    @RequestMapping(value = "test3", method = RequestMethod.GET)
    @XXSecurity({RoleConstant.Four})
    public String test3(@RequestHeader("token")String token) {
        Long userId = JWTUtil.unsign(token,Long.class);
        UserEntity userEntity = userService.getUserById(userId);
        return "Ok";
    }

}

然后写一个RoleConstant类,将权限对应的值放在其中,这里仅作简单的演示。

public class RoleConstant {
    public static final long One = 1l;
    public static final long Two = 2l;
    public static final long Three = 3l;
    public static final long Four = 4l;
}

然后到配置中,设置test1不需要被拦截,test2和test3需要被拦截

<!-- 配置拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 拦截以下请求,/**即拦截所有 -->
        <mvc:mapping path="/**"/>
        <!-- 不拦截以下强求 -->
        <mvc:exclude-mapping path="/user/test1" />
        <bean class="com.cloneZjrt.util.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
第三步,解析注解——在编译期或运行时检测到标记,并进行特殊操作。

这里,我将注解的验证放在拦截器中,对掩护权限进行拦截

重写HandlerInterceptor,对权限进行解析和验证

public class MyInterceptor implements HandlerInterceptor {

    @Autowired
    private UserService userService;

    //请求发送到Controller之前调用
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {

        /**
         * 配置跨域
         */
        httpServletResponse.setHeader("Access-Control-Allow-Origin","*");//允许所有域名访问
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
        httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, token");
        httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");

        /**
         * 根据自己的业务编写代买
         * 以验证请求头为例
         */
        if(httpServletRequest.getHeader("token") == null){
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json; charset=utf-8");

            HashMap<String,String> returnJson = new HashMap<>();
            returnJson.put("status","415");
            returnJson.put("message","拒绝");
            returnJson.put("data",null);

            PrintWriter out;
            out = httpServletResponse.getWriter();
            out.append(returnJson.toString());

            return false;
        }

        String token = httpServletRequest.getHeader("token");
        if (token == null) {
            //这里应该用自定义异常
            httpServletResponse.getWriter().write("未登录,请重新登录后操作");
            return false;
        } else {
            //这里的JWTUtil工具类仅作简单的加密和解密,不做具体介绍
            //长寿运行程序时,可直接使header中的token等于userId,跳过解密这一环节
            //这一块代码过于繁琐,也可写在单独的方法里。
            Long userId = JWTUtil.unsign(token,Long.class);
            if (userId == null || userService.getUserById(userId) == null) {
                System.out.println("非法用户");
            }
            //获取请求用户的权限列表,这里是权限的ID列表,对应RoleConstant类中的属性
            List<Long> roleList = userService.getRoleIdByUserId(userId);
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                XXSecurity eMethod = handlerMethod.getMethodAnnotation(XXSecurity .class);
                if (eMethod != null) {
                    //验证用户是否有方法注解上所需要的权限
                    long[] value = eMethod.value();
                    if (value.length == 0) {
                        return true;
                    }
                    //注解中的权限包含该用户的权限就直接放心
                    for (long i : value) {
                        if (roleList.contains(i)) {
                            return true;
                        }
                    }
                    System.out.println("权限不足");
                    return false;
                } else {
                    //拿到类上的注解,和上面同理
                    XXSecurity eType = handlerMethod.getMethod().getDeclaringClass().getAnnotation(XXSecurity .class);
                    if (eType != null) {
                        long[] value = eType.value();
                        if (value.length == 0) {
                            return true;
                        }
                        for (long i : value) {
                            if (roleList.contains(i)) {
                                return true;
                            }
                        }
                        System.out.println("权限不足");
                        return false;
                    } else {
                        return true;
                    }
                }
            }
            //走到这里说明在方法和类上都没有权限注解,直接放行
            return true;
        }
    }

    //请求发送到Controller之后调用
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    //完成请求的处理的回调方法
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}
  • 第四步,验证

在数据库中,用户ID为“1”的用户只有“One”“Two”“Three”三个权限

1.访问:http://localhost:8888/cloneZjrt/user/test?userId=1

打印:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODMxMjkzOTYxMTMsInBheWxvYWQiOiIxIn0.2cky7JbAFvxD8elZvAgAa5fFKgOupxrxa9X9vojdtkg

2.访问:http://localhost:8888/cloneZjrt/user/test2

headers中设置

[{“key”:“Content-Type”,“value”:“application/json”,“description”:"",“enabled”:true},{“key”:“token”,“value”:“eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODMxMjkzOTYxMTMsInBheWxvYWQiOiIxIn0.2cky7JbAFvxD8elZvAgAa5fFKgOupxrxa9X9vojdtkg”,“description”:"",“enabled”:true}]

打印:OK

3.访问:http://localhost:8888/cloneZjrt/user/test3

headers中设置

[{“key”:“Content-Type”,“value”:“application/json”,“description”:"",“enabled”:true},{“key”:“token”,“value”:“eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODMxMjkzOTYxMTMsInBheWxvYWQiOiIxIn0.2cky7JbAFvxD8elZvAgAa5fFKgOupxrxa9X9vojdtkg”,“description”:"",“enabled”:true}]

打印:权限不足

当然,也可以在测试类中,通过反射的方法进行验证

@RunWith(SpringJUnit4ClassRunner.class)
//告诉junit spring配置文件
@WebAppConfiguration
@ContextConfiguration({"classpath*:applicationContext.xml"})
public class TestDAO {
    
    public void testOne() throws Exception {
        try {
            //获取UserController对象
            Class userController = Class.forName("com.cloneZjrt.controller.UserController");

            //这里形参为String.class,因为我这个方法返回的是String
            Method userMethod = userController.getMethod("test2", String.class);

            if(userMethod.isAnnotationPresent(XXSecurity.class)){
                System.out.println("UserController类上配置了XXSecurity注解!");
                //获取该元素上指定类型的注解
                XXSecurity xxSecurity = userMethod.getAnnotation(XXSecurity.class);
                System.out.println("RoleConstant: " + xxSecurity.value());
                //在注解中,我设置的value属性是是一个数组,遍历一下全部打印
                for(Long l : xxSecurity.value()){
                    System.out.println(l);
                }
            }else{
                System.out.println("UserController类上没有配置XXSecurity注解!");
            }
        } catch (LogicException e) {
            e.printStackTrace();
        }
    }
}

打印

UserController类上配置了XXSecurity注解!
RoleConstant: [J@3163987e
1
2

验证通过

参考博文:SSM项目服务端鉴权(自定义注解实现)

自定义注解详细介绍

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值