Java中使用 jwt + 拦截器配置 超级详细教程(详细源码)

一、创建JWT

1.导入依赖

maven

<!--引入JWT依赖,由于是基于Java,所以需要的是java-jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

gradle

implementation 'io.jsonwebtoken:jjwt:0.9.1'

implementation 'com.auth0:java-jwt:3.4.0'

2.创建jwt工具类

package com.zx.framework.util;

import com.zx.cargo.pojo.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * jwt工具类 生成、解析、校验token
 */
public class JwtUtil {
	/**
     * 用户登录成功后生成jwt
     * 使用Hs256算法
     * 三部分组成 头部+荷载+签证信息
     * @param ttlMillis jwt过期时间
     * @param user      登录成功的user对象
     * @return
     */
    public static String createJwt(long ttlMillis, User user){
        // header部分,jwt已经封装好了
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        // jwt生成时间 当前时间
        long nowMillis = System.currentTimeMillis();
        Date date = new Date(nowMillis);

        // payload 荷载部分(存放有效信息的地方,包含标准中注册的声明、公共声明、私有声明)
        // 创建私有声明
        Map<String,Object> claims = new HashMap<>();
        claims.put("id", user.getUserId());
        claims.put("username",user.getUserName());
        claims.put("password",user.getUserPassword());

        // 生成秘钥secret用
//        String key = user.getUserPassword();
        byte[] bytes = user.getUserPassword().getBytes();
        // 生成签发人
        String subject = user.getUserName();

        // 为payload添加标准声明和私有声明(new一个JwtBuilder,设置jwt的body)
        JwtBuilder jwtBuilder = Jwts.builder()
                // 先设置自己创建的私有声明,要是写在标准声明后面,会覆盖掉标准声明
                .setClaims(claims)
                // 设置jti(jwt id),主要用来作为一次性token,从而回避重放攻击
                .setId(UUID.randomUUID().toString())
                // 设置iat jwt签发时间
                .setIssuedAt(date)
                // 设置jwt的所有人
                .setSubject(subject)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, bytes);
        // 设置jwt的过期时间
        if(ttlMillis>= 0){
            long expMillis = ttlMillis+nowMillis;
            Date expDate = new Date(expMillis);
            jwtBuilder.setExpiration(expDate);
        }
        System.out.println("生成jwt");
        return jwtBuilder.compact();
    }

	/**
     * 解密jwt
     * @param token 需要被解密的token
     * @param user 用户的对象
     * @return
     */
    public static Claims parseJWT(String token,User user){
        // 签名秘钥(与生成签名的秘钥一样)
        String key = user.getUserPassword();

        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(key)
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }


	/**
     * 校验jwt
     * 判断token携带的密码跟数据库里的是否一致(也可用官方的校验方法)
     * @param token
     * @param user
     * @return
     */
    public static Boolean isVerify(String token,User user){
        // 秘钥
        byte[] bytes = user.getUserPassword().getBytes();

        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(bytes)
                // 设置需要解析的jwt
                .parseClaimsJws(token)
                .getBody();

        System.out.println("claims-----》"+claims);

        // 判断密码是否一致
        if(claims.get("password").equals(user.getUserPassword())){
            return true;
        }

        return false;
    }
}

二、配置拦截器

1.配置拦截器文件

package com.zx.framework.config;

import com.zx.framework.interceptor.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * jwt 后台访问拦截
 * 拦截器配置文件 config
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册TestInterceptor拦截器
        InterceptorRegistration registration = registry.addInterceptor(authenticationInterceptor());
        registration.addPathPatterns("/**");//所有路径都被拦截
        registration.excludePathPatterns("" +
                "/assets/**",             // assets文件夹里文件不拦截
                "/**/*.js",              //js静态资源不拦截
                "/**/*.css"             //css静态资源不拦截
        );
    }
}

2.创建拦截器类

package com.zx.framework.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.zx.cargo.pojo.User;
import com.zx.cargo.stock.service.UserLoginService;
import com.zx.framework.annotation.PassToken;
import com.zx.framework.annotation.UserLoginToken;
import com.zx.framework.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

public class AuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    private UserLoginService userLoginService;
    /*
    controller执行之前
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object){
        // 从http请求头中取出token
        System.out.println("输出token"+request.getHeader("token"));
        String token = request.getHeader("token");
        // 如果不是映射到方法直接通过
        if(!(object instanceof HandlerMethod)){
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        // 方法是否带有UserLoginToken注释
        if(method.isAnnotationPresent(UserLoginToken.class)){
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if(userLoginToken.required()){
                return true;
            }
        }

        // 方法是否带有PassToken注释
        if(method.isAnnotationPresent(PassToken.class)){
            PassToken passToken = method.getDeclaredAnnotation(PassToken.class);
            if(passToken.required()){
                // 执行认证
                if(token == null){
                    throw new RuntimeException("无token,请重新登录!");
                }
                // 获取token中的userId
                String userId;
                try {
                    userId = JWT.decode(token).getClaim("id").asString();
                } catch (JWTDecodeException j){
                    throw new RuntimeException("访问异常!");
                }
                User user = userLoginService.findUserById(userId);
                if(StringUtils.isEmpty(user)){
                    throw new RuntimeException("当前用户不存在,请重新登录!");
                }
                Boolean verify = JwtUtil.isVerify(token, user);
                if(!verify){
                    throw new RuntimeException("非法访问!");
                }
                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 {

    }
}

三、创建使用注解

创建两个注解,PassToken和UserLoginToken,用于在项目开发中,如果需要权限校验就标注userlogintoken,如果访问的资源不需要权限验证则正常编写不需要任何注解,如果用的的请求时登录操作,在用户登录的方法上增加passtoken注解。

1.除登录外使用的注解

package com.zx.framework.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PassToken {
    boolean required() default true;
}

2.登录使用的注解

ackage com.zx.framework.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UserLoginToken {
    boolean required() default true;
}

注解解析:从上面我们新建的两个类上我们可以看到主要的等学习到的就四点
第一:如何创建一个注解
第二:在我们自定义注解上新增@Target注解(注解解释:这个注解标注我们定义的注解是可以作用在类上还是方法上还是属性上面)
第三:在我们自定义注解上新增@Retention注解(注解解释:作用是定义被它所注解的注解保留多久,一共有三种策略,SOURCE 被编译器忽略,CLASS 注解将会被保留在Class文件中,但在运行时并不会被VM保留。这是默认行为,所有没有用Retention注解的注解,都会采用这种策略。RUNTIME 保留至运行时。所以我们可以通过反射去获取注解信息。
第四:boolean required() default true; 默认required() 属性为true

在这里插入图片描述
在这里插入图片描述

对应方法加入对应注解即可

  • 9
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

国家级著名CV工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值