springboot整合jwt在AOP层进行校验

1:引入jar包

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.4.0</version>
</dependency>

2:新建两个接口文件-需要自定义两个注解

package com.noodle.shop.common.VaToken;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
   
//用来跳过验证的PassToken(在controll层上加上这个注解就不会校验token)
 @Target({ElementType.METHOD, ElementType.TYPE})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface PassToken {
     boolean required() default true;
 }
package com.noodle.shop.common.VaToken;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//需要登录才能进行操作的注解UserLoginToken(加上这个注解就会去校验token)
 
 @Target({ElementType.METHOD, ElementType.TYPE})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface UserLoginToken {
     boolean required() default true;
 }

@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)——包

@Retention:注解的保留位置
RetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略,在class字节码文件中不包含。
RetentionPolicy.CLASS:这种类型的Annotations编译时被保留,默认的保留策略,在class文件中存在,但JVM将会忽略,运行时无法获得。
RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解

3:在userService中写一个制造token的方法

/**
 * 生成token
 * @param user
 * @return
 */
public String makeToken(VaUserEntity user) {
    //这里用的密钥是 iiiii 可根据用户密码啊等等生成,都没关系
    //withAudience()存入需要保存在token的信息,这里我把用户ID存入token中
    String token="";
    Date date = new Date(System.currentTimeMillis()+1000*60*2*24);
    token= JWT.create()
            .withAudience(user.getId())
            .withExpiresAt(date)
            .sign(Algorithm.HMAC256("iiiii"));
    return token;
}

4:写一个controll层

package com.noodle.shop.controller;

import com.noodle.shop.common.VaToken.UserLoginToken;
import com.noodle.shop.common.util.Message;
import com.noodle.shop.common.util.Messages;
import com.noodle.shop.entity.AddressEnitity;
import com.noodle.shop.entity.VaUserEntity;
import com.noodle.shop.service.user.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Slf4j
@Controller
@RequestMapping("/user")
@CrossOrigin
public class Goods {
    @Autowired
    private UserService userService;

    @RequestMapping("/login")
    @ResponseBody
    public Message login(StringBuilder json) {
        //将token返回前端,以后前端每次访问都带着token在请求头上我解析
        VaUserEntity user=new VaUserEntity();
        user.setId(9);
        //调刚刚上面定义的userService中的制造token的方法得到token
        String token=userService.makeToken(user);
        System.out.printf(token);
        return Messages.success(token);
    }

    @RequestMapping("/index")
    @ResponseBody
    //有这个玩意就代表这个接口要经过token校验,上面的那个登录接口没得这个玩意就不需要校验,或者加一个 @PassToken 也会跳过校验
    @UserLoginToken
    public Message indexGoods(AddressEnitity address, VaUserEntity va) {
        //address:是用户真实传的数据
        //va:是我凭token在切面层解析出来的用户再赛进来的用户数据
        System.out.println(address);
        System.out.println(va);
        return Messages.failed(400,"dsfad");
    }
}

5:加入AOP 支持JAR包

<dependency>
   <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.1.6.RELEASE</version>
</dependency>

6:写AOP层代码进行校验token

package com.noodle.shop.common.aop;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.noodle.shop.common.Exception.BizException;
import com.noodle.shop.common.VaToken.PassToken;
import com.noodle.shop.common.VaToken.UserLoginToken;
import com.noodle.shop.common.util.*;
import com.noodle.shop.entity.AddressEnitity;
import com.noodle.shop.entity.VaUserEntity;
import com.noodle.shop.service.user.UserService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;


@Component
@Aspect
@Slf4j
public class ExceptionAop {

    @Autowired
    private UserService userService;

	//定义切面,所有的controller层都会监控
    @Pointcut("execution(* com.noodle.shop.controller..*.*(..))")
    public void doHander() {

    }

    @Around("doHander()")
    public Object exception(ProceedingJoinPoint joinPoint) {
        try {
            //进入controller层前
            beforePoint(joinPoint);
            //放行
            Object result = joinPoint.proceed();
            //返回数据前
            afterPoint(joinPoint, result);
            return result;
        } catch (BizException e) {
            return Messages.failed(e.getCode(), e.getMessage());
        } catch (Throwable e) {
            return Messages.failed(9999, "系统异常");
        }

    }

    private Boolean beforePoint(ProceedingJoinPoint joinPoint) throws Exception {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        
        // 从 http 请求头中取出 token
        String token = request.getHeader("token");
        
        //得到要进入的是哪个controller方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        //检查是否有passtoken注释,有则跳过认证,所以在controller层加了@Passtoken注解,这里我就不校验
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        
        //加了@UserLoginToken注解的我要进行校验
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new RuntimeException("无token,请重新登录");
                }
                // 获取 token 中的 user id
                String userId;
                try {
                    userId = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                // 验证 token  这里的 iiiii 要和上面生成token的密钥一致才能解析成功
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("iiiii")).build();
                try {
                    //验证token
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("401");
                }
                //得到这个方法控制器的所有形参
                Object[] args = joinPoint.getArgs();
                for (Object argItem : args) {
                    //如果这个控制器方法中有用户这个形参,说明这个控制器需要用户的信息,那么我就把我这里解析出来的userId 赛进这个形参中,那样在控制器那边就能得到我赛的userId了
                    if (argItem instanceof VaUserEntity) {
                        VaUserEntity paramVO = (VaUserEntity) argItem;
                        //paramVO.setPassword("000000");
                        paramVO.setId(userId);
                    }
                }
                return true;
            }
        }
        return true;
    }


    private void afterPoint(ProceedingJoinPoint joinPoint, Object result) throws Exception {

    }

}

使用jwt就这么简单,同时还讲解了怎么在AOP层对参数值进行修改和增加

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值