自定义注解:springboot+vue-限制接口调用

前言

公司前端项目用的是vue,后端用的是Springboot。因为最近公司业务的原因,需要根据条件限制接口的调用。限制的条件是根据指定的key获取Redis中value的值,然后判断value中的日期往后推一年(例如value中的日期是:2018-09-12,往后推一年就是2019-09-12)是否大于当前日期。如果大于则可访问(这里的可访问指的是可访问所有接口)。反之,则所有接口不可访问。

在使用自定义注解之前,我们先来了解Java为我们提供的元注解和相关定义注解的语法。

一、了解Java注解语法

1.元注解(meta-annotation)

元注解的作用就是负责注解其他注解,在java.lang.annotation包中可以找到。

  1. @Target
  2. @Retention
  3. @Documented
  4. @Inherited

二、每个元注解的作用

1.@Target:

用于描述注解的使用范围。

参数说明:

@Target(ElementType.TYPE) //接口、类、枚举、注解 @Target(ElementType.FIELD) //字段、枚举的常量 @Target(ElementType.METHOD) //方法 @Target(ElementType.PARAMETER) //方法参数 @Target(ElementType.CONSTRUCTOR) //构造函数 @Target(ElementType.LOCAL_VARIABLE) //局部变量 @Target(ElementType.ANNOTATION_TYPE) //注解 @Target(ElementType.PACKAGE) //包

2.@Retention

表示需要在什么级别保存该注释信息,用于描述注解的生命周期。

RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃。 RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期。 RetentionPolicy.RUNTIME:注解在运行时有效(运行时保留)。

3.@Documented

Documented是一个标记注解,没有成员。

4.@Inherited

@Inherited 表示该注解会被子类继承。仅针对类,成员属性、方法并不受此注释的影响。对于类来说,子类要继承父类的注解需要该注解被 @Inherited 标识。对于成员属性和方法来说,非重写的都会保持和父类一样的注解,而被实现的抽象方法,被重写的方法都不会有父类的注解。

5.自定义注解

使用@interface自定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型。

三、自定义注解

import java.lang.annotation.*;/** *定制一个接口 */@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface EnableAuth {}
    public class ApiAuthDataInit implements ApplicationContextAware {
        /** 存放需要权限拦截的接口uri */        public static List<String> checkApis = new ArrayList<>();
        @Override            public void setApplicationContext(ApplicationContext ctx) throws BeansException {                Map<String, Object> beanMap = ctx.getBeansWithAnnotation(RestController.class);                if (beanMap != null) {                    for (Object bean : beanMap.values()) {                        Class<?> clz = bean.getClass();                        Method[] methods = clz.getMethods();                        for (Method method : methods) {                            if (method.isAnnotationPresent(EnableAuth.class)) {                                String uri = getApiUri(clz, method);                                checkApis.add(uri);                            }                        }                    }                }            }
            private String getApiUri(Class<?> clz, Method method) {                StringBuilder uri = new StringBuilder();                uri.append(clz.getAnnotation(RequestMapping.class).value()[0]);                if (method.isAnnotationPresent(GetMapping.class)) {                    uri.append(method.getAnnotation(GetMapping.class).value()[0]);                } else if (method.isAnnotationPresent(PostMapping.class)) {                    uri.append(method.getAnnotation(PostMapping.class).value()[0]);                }//                else if (method.isAnnotationPresent(PutMapping.class)) {//                    uri.append(method.getAnnotation(PutMapping.class).value()[0]);//                }else if (method.isAnnotationPresent(DeleteMapping.class)) {//                    uri.append(method.getAnnotation(DeleteMapping.class).value()[0]);//                }                else if (method.isAnnotationPresent(RequestMapping.class)) {                    uri.append(method.getAnnotation(RequestMapping.class).value()[0]);                }                return uri.toString();            }
        }

当一个类实现了ApplicationContextAware接口之后,Spring容器会自动检测所有的Bean,如果发现某个Bean实现了ApplicationContextAware接口,Spring容器就会在创建了该Bean之后,自动调用该Bean的setApplicationContextAware()方法。

在setApplicationContextAware()方法中获取所有带有@RestController注解的类,并获取该类下所有带有@EnableAuth注解的方法,获取该方法@RequestMapping的uri路径,并将uri存入checkApis中。

public class ApiFilter implements Filter {    @Autowired    private  RedisService redisService;
    @Override    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)            throws IOException, ServletException {        HttpServletRequest req = (HttpServletRequest) request;        HttpServletResponse resp = (HttpServletResponse) response;        resp.setCharacterEncoding("utf-8");        resp.setContentType("application/json;charset=utf-8");        // 判断checkApis中是否包含当前请求的uri        if (ApiAuthDataInit.checkApis.contains(req.getRequestURI())) {            String key = "Devices:xxxxxxx";            CarsDto carsDto = redisService.getCar(key);            Calendar c = Calendar.getInstance();            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");            String time = carsDto.getDevFixTime().substring(0,10);            Date date = null;            try {                date = sdf.parse(time);            } catch (ParseException e) {                e.printStackTrace();            }            //设置日历时间            c.setTime(date);            c.add(Calendar.MONTH,18);            String strDate = sdf.format(c.getTime());            String nowDate = sdf.format(new Date());                long nowTime = Long.valueOf(nowDate.replaceAll("[-\\s:]",""));                long carTime = Long.valueOf(strDate.replaceAll("[-\\s:]",""));                if (nowTime > carTime) {                   return;                }        }        chain.doFilter(request, response);    }
    @Override    public void destroy() {
    }
}

定义了注解,并在需要的时候给相关类,类属性加上注解信息,如果没有响应的注解信息处理流程,注解可以说是没有实用价值。

四、使用自定义注解

   @EnableAuth   @PostMapping(value = "/login")  public ResultData login(String username,  String password) {        logger.info("------用户登录 login:{}   start", "username:" + username + "password:" + password);        User user = userDao.findByUserName(username);        if (user == null) {            logger.info("------user == null");            return ResultData.bizError(ResultMessage.USER_NOT_FIND);        }        if (!user.getStatus().equals(UserStatus.ACTIVE.getCode())) {            logger.info("------user已删除");            return ResultData.bizError(ResultMessage.USER_NOT_FIND);//被禁止的异常        }        if (!loginService.checkUser(user, password)) {            logger.info("------用户名或密码不正确");            return ResultData.bizError(ResultMessage.USER_PASSWORD_EQUAL);        }        // 设置登陆时间        logger.info("------更新登录时间");        loginService.setLoginTime(user);        logger.info("------用户登录 login:{} 登录成功", "user:" + new Gson().toJson(user));        LoginUser loginUser = new LoginUser();      //略
        logger.info("------用户登录 login:{}   end", "loginUser:" + new Gson().toJson(loginUser));        return ResultData.ok().putDataValue("User", loginUser);    }

五、vue部分代码

<template>    <div id="login">        <div class="loginBox">            <div class="loginForm">                <div class="formHeader">                    <span class="title">欢迎登录</span>                </div>                <el-form :model="loginForm" ref="loginForm" :rules="loginFormRule">                    <el-form-item label="" prop="username">                        <i class="iconfont icon-touxiang"></i>                        <el-input v-model="loginForm.username" placeholder="用户名" auto-complete="off"></el-input>                    </el-form-item>                    <el-form-item label="" prop="password">                        <i class="iconfont icon-mima"></i>                        <el-input v-model="loginForm.password" placeholder="密码" type="password" auto-complete="off"></el-input>                    </el-form-item>                    <el-form-item>                        <el-button class="loginBtn" type="primary" @click="login">登录</el-button>                    </el-form-item>                </el-form>            </div>        </div>    </div></template>
<script>
    export default {        data () {           //略        },       //略        methods: {            //略            login(){                this.$refs['loginForm'].validate((valid) => {                    if(valid){                        this.api.login(this.loginForm).then((res)=> {                            if (res.data.code=='OK') {                                this.$message({                                    type:'success',                                    message:'登录成功!'                                });                                this.$store.commit('signIn', res.data.data.User);                            } else {                                this.$message.error(res.data.message);                            }                        }).catch((res)=>{                                this.$message.error(res);                        });                    }                });            }        }    }</script>

本文分享自微信公众号 - JAVA葵花宝典(Javakhbd)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏chenchenchen

@Autowired:构造函数注入和变量注入

public TestController(TestService testService) {

27830
来自专栏IT码农

git部署线上项目

2.还有就是目录的所有者何所有组的问题chowm -R git:git 目录

13730
来自专栏BAT的男人

【小家java】泛型--那些年我们一起躺过的坑

java的泛型是在JDK5开始出现的,在各种设计模式中有非常广泛的应用,比如设计一些基类等,参数化类型(具体的类型参数化)对扩展性能提供很好的支持,避免了不安全...

10820
来自专栏IT码农

【使用Postman测试web接口】管理http请求

Postman程序窗口可以划分为3个区域,包括Sidebar、请求编辑区、请求响应区。在Sidebar部分包含“History”和“Collection”选项卡...

8020
来自专栏BAT的男人

【小家java】对java中null、void、Void的理解学习

本篇博文很简单啊,主要说说咱们平时最长看见的null、void和Void等。一般人可能不会留意,但此文通过一些简单的例子,希望可以加深同学们对他哥几个的了解

9030
来自专栏code秘密花园

如何写出一个惊艳面试官的深拷贝

最近经常看到很多 JavaScript手写代码的文章总结,里面提供了很多 JavaScriptApi的手写实现。

12920
来自专栏程序猿杂货铺

使用devtools导致的类型转换异常及Spring Devtools 源码初步解析

SpringBoot项目中的热部署引发的血的教训,报错代码如下(第6行强制转换,明确可以肯定此处Object肯定是 UserInfoDTO):

16230
来自专栏BAT的男人

【小家java】引用类型(强引用、软引用、弱引用、虚引用)

本文不论述java中值传递和引用传递之间的问题(有需求的可移步理解java中值传递和引用传递),而重点讨论Java中提供了4个级别的引用:强应用、软引用、弱引用...

8340
来自专栏程序猿杂货铺

java8新特性(二):StreamAPI

Java8中有两大最为重要的改变。第一个是Lambda 表达式;另外一个则是Stream API(java.util.stream.*)。 Stream 是J...

8130
来自专栏BAT的男人

【小家java】静态类、静态方法、内部类、匿名内部类、非静态类的一些实践

静态内部类的作用:只是为了降低包的深度,方便类的使用,实现高内聚。静态内部类适用于不依赖于外部类,不用使用外在类的非静态属性和方法,只是为了方便管理类结构而定义...

26430

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励