springboot使用自定义注解,通过aop实现注解业务

前言

本文章介绍通过自定义的注解实现判断用户token是否失效的业务,代码很简单知识点分为以下几个

  • 自定义注解
  • aop around
  • token机制的简单说明,这个会在后期做微服务的时候在详细讲解
  • base64加密、解密

一、软件架构&版本

  • 简单的mvc模式
  • 使用springboot 2.5.2
  • java 1.8

二、安装教程

  1. 本地需要有java,maven环境
  2. 下载项目gitee
  3. 使用你的idea运行起来
  4. 本项目使用的是web服务,在配置文件可修改端口

三、项目讲解

1. 自定义注解

1.1 代码

team/exampl/springannotation/annotation/CheckToken.java


/**
 * 检查token
 *
 * @author cheer
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CheckToken {
    /**
     * 在使用时要指定一个token验证类型
     * 默认为检查用户失效验证
     *
     * @return token type
     */
    TokenTypeEnum value() default TokenTypeEnum.USER_INVALIDATION;
}

1.2 知识点

  1. RetentionPolicy:指定RUNTIME类型在VM里保留,所以可以使用反射获取到
  2. Target:指定为在方法上使用
  3. TokenTypeEnum:自定义一个枚举是为了拓展业务

2. 实现aop

2.1 代码

team/exampl/springannotation/aop/MyAspect.java


/**
 1. aop
 2. @author cheer 
 */
@Slf4j
@Component
@Aspect
public class MyAspect {
    @Around("execution(* team.exampl.springannotation.cotroller..*.*(..))")
    public Object before(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature sig = (MethodSignature) joinPoint.getSignature();
        log.info("切入的方法名:{}", sig.getMethod().getName());
        //获取方法上注解
        CheckToken[] annotation = sig.getMethod().getAnnotationsByType(CheckToken.class);
        if (annotation.length > 0) {
            //获取到参数
            Object arg = joinPoint.getArgs()[0];
            if (arg instanceof BaseDTO) {
                BaseDTO baseDTO = (BaseDTO) arg;
                if (baseDTO.getToken() == null) return new BaseVO<>(401, "token null", null);
                String token = Base64Util.decoder(baseDTO.getToken());
                for (CheckToken checkToken : annotation) {
                    TokenTypeEnum typeEnum = checkToken.value();
                    if (typeEnum == TokenTypeEnum.USER_INVALIDATION) {
                        //验证token,这里就不做这么多验证token逻辑了。TODO 后期新建一个spring cloud项目讲清楚token的使用
                        if (CacheUtil.checkUserInvalidation(token)) {
                            return joinPoint.proceed();
                        }
                        return new BaseVO<>(401, "token无效,请重新登录", null);
                    } else if (typeEnum == TokenTypeEnum.USER_RE_LOGIN) {
                        //删除用户token
                        if (CacheUtil.checkUserInvalidation(token)) {
                            CacheUtil.getTokenMap().remove(token);
                            return joinPoint.proceed();
                        }
                        return new BaseVO<>(401, "token无效,请重新登录", null);
                    }
                }
            }
        }
        return joinPoint.proceed();
    }
}

2.2 知识点

  1. Around:实现环绕切入是为了把整个业务做完整,因为使用了token验证作为业务场景,所以这个验证业务在aop里必须是完整的。不明白,有疑问?请看第3条。
  2. getAnnotationsByType:根据注解查找这个方法是否存在你的自定义注解,注解可以是多个因为一个方法上可以实现两个业务不同的相同名称注解。这就是为什么使用自定义枚举TokenTypeEnum当作注解参数了。
  3. return new BaseVO:因为实现了Around方式的切入我们就可以在这里做到一个完整的业务,所以我们就可以在这返回业务response,从而减少代码冗余。

3. token测试

3.1 先明白为什么要使用token,什么场景需要token呢?

以下要讲一些token的业务以及简单的实现,选中的是本项目已经可以实现的。

  • token可以存放一些简单的用户信息,前端不必每次请求都携带用户的信息
  • 它可以在多个服务作为共享信息
  • 解决CSRF(Cross-site request forgery),中文名称:跨站请求伪造
  • 可以作为单点登录

3.2 UserController代码

team/exampl/springannotation/cotroller/UserController.java

    /**
     * 不需要检验token的方法
     *
     * @param dto 用户
     * @return 认证
     */
    @GetMapping("authentication")
    public BaseVO<UserLoginVO> authentication(UserDTO dto)

    /**
     * 需要检验token的方法
     *
     * @param dto 用户
     * @return 用户
     */
    @CheckToken
    @GetMapping("getUserInfo")
    public BaseVO<UserInfoVO> getUserInfo(UserDTO dto)

3.3 UserController知识点

  1. 在方法上使用自定义注解就可以在MyAspect里判断token

3.4 UserServiceImpl代码

team/exampl/springannotation/service/impl/UserServiceImpl.java


    final String account = "test";
    final String password = "test123";

    @Override
    public UserLoginVO authentication(UserDTO dto) {
        //假设有一个用户账号密码是test,test
        if (dto.getAccount().equals(account) && dto.getPassword().equals(password)) {
            UserLoginVO vo = new UserLoginVO();
            //这里的token先存放一个用户账号,一般token会存放用户的基础信息。经过加密后传给前端
            vo.setToken(Base64Util.encoder(account));
            //我们使用用户的账号当作缓存token的key
            CacheUtil.getTokenMap().put(account, password);
            return vo;
        }
        return null;
    }

	@Override
    public UserInfoVO getUserInfo(UserDTO dto) {
        //假如这是数据库或缓存,正好有一条用户信息,并且是根据account查询用户
        Map<String, UserInfoVO> userMap = new HashMap<>();
        userMap.put(account, new UserInfoVO("KING"));

        //这里使用token获取用户的基础信息
        String token = Base64Util.decoder(dto.getToken());

        //已知token存放的是用户账号那么就可以按照这个账号查找用户是否存在并返回用户信息
        if (userMap.containsKey(token)) return userMap.get(token);
        return null;
    }

3.5 UserServiceImpl知识点

  1. 使用base64加密用户信息,其实加密token是比这复杂的,服务器要生成签名对前端发过来的token进行验证等一些操作,这个项目只做了加密简单的例子能先理解token,注意这样加密没有安全性
  2. 每次生成token后要存起来。建议存在redis,因为在使用微服务时多个服务可以共享token。
  3. getUserInfo方法可以直接使用decoder解密token,因为它在被controoler调用的时候就已经被MyAspect验证过token信息是否已经存在了

3.6 postman test

以下测验token的使用流程

步骤一:正确访问:http://localhost:10086/user/authentication?account=test&password=test123
访问成功得到token
访问成功得到token,成功之后在访问需要验证token的接口,一定要把token传过去

步骤二:正确访问:http://localhost:10086/user/getUserInfo ?token=dGVzdA==
可以得到用户name
可以得到用户name,如果没有先执行步骤一接口会出现错误信息如下
token无效,请重新登录

对文章有疑问或者有指正的地方欢迎联系
END

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值