后端学习1.3 利用JWT生成、验证token;对特定接口配置拦截器

功能要求

任务:

1. 利用JWT生成token,token中包含用户名等信息。实现token反解析用户名功能。
2. 登录时返回token,并且设置登录半小时之后自动退出登录。
3. 配置拦截器,用户工作日志接口需在header中提供Token才能放行。
请利用拦截器和方法注解验证Token是否过期、是否提供。
若未提供,请抛出异常状态。

开发工具:IDEA
技术:Springboot+postman+Jwt

1、添加依赖


        <dependency>
            <groupId>com.auth0groupId>
            <artifactId>java-jwtartifactId>
            <version> 3.10.0version>
        dependency>

2、利用JWT生成、解析、验证token

package com.example.demo.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.stereotype.Repository;

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

/**
 * @Author 
 * @Date 2022/8/1 10:06
 * @Description 利用Jwt生成token
 */

@Repository
public class JwtUtils {

    private static final String SECRET_KEY = "login";//自定义密钥
    private static final long TOKEN_EXPIRE_TIME = 1800000L ;//设置过期时间30min

    /**
     * 生成 jwt token
     */
    public static String creatJwtTo(int id,String userId,String username){

        Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);//SECRET_KEY自定义密钥
        // 设置头信息
        HashMap<String, Object> header = new HashMap<>(2);
        header.put("typ","JWT");
        header.put("alg","HS256");

//        TokenVo tokenVo = new TokenVo();
        // 生成 Token
        String token = JWT.create().withHeader(header)

                .withClaim("id", id)//自定义参数
                .withClaim("userId", userId)//自定义参数
                .withClaim("userName",username)//自定义参数
                .withExpiresAt(new Date(System.currentTimeMillis() + TOKEN_EXPIRE_TIME)).sign(algorithm);//TOKEN_EXPIRE_TIME token过期时间 毫秒
        return token;
    }

    /**
     *  解析 jwt
     */
    public static Map<String, Claim> parseJwtTo(String token){
        Map<String, Claim> claims = null;
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);//SECRET_KEY自定义密钥
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);//需要解析token
            claims = jwt.getClaims();
            return claims;
        } catch (Exception e) {
            return null;
        }
    }

    /**@RequestHeader("token")
     * 验证token合法性
     */
    public static void verify(String token) throws Exception{

        try {
            DecodedJWT verify = JWT.require(Algorithm.HMAC256(SECRET_KEY )).build().verify(token);
            System.out.println(verify);
            int id=verify.getClaim("id").asInt();
            String userId = verify.getClaim("userId").asString();
            String username = verify.getClaim("userName").asString();

            System.out.println(id);
            System.out.println(userId);
            System.out.println(username);
        }catch (TokenExpiredException e) {
            throw  new TokenExpiredException("令牌过期");
        } catch (JWTVerificationException e) {
            throw  new RuntimeException("令牌不正确");
        }catch (NullPointerException e){
            throw new NullPointerException("缺少token");
        }
    }
}

3、登陆时,返回token

//登录
    @PostMapping(value = "/login")
    public Map<String,Object> login(@RequestBody Map<String, String> person) throws ServiceException{
        String username=person.get("username");
        String password=person.get("password");
        //1. 判断用户名、密码是否为空
        if (username != null && password != null) {
            List<User> users = service.queryByUserName(username);
            //2. 判断用户名是否存在
            if (users != null && users.size() > 0) {
                User user = users.get(0);
                //3. 判断密码是否正确
                RedisUtils red=new RedisUtils();
                int i= red.checkUser(user,password);
                if(i==0){
                    int id=user.getId();
                    String userId=user.getUserId();
                    String token= JwtUtils.creatJwtTo(id,userId,username);
                    Map<String,Object> data=new HashMap<>();
                    data.put("token",token);
                    return data;
                }else if(i==1){
                    throw new ServiceException(500,"密码错误!登录失败");
                }else{
                    throw new ServiceException(500,"登录异常,账户锁定,请等待系统解锁");
                }
            } else {
                throw new ServiceException(500,"登陆失败,用户名不存在");
            }
        } else {
            throw new ServiceException(500,"登陆失败,请检查用户名、密码是否为空");
        }
    }
}

后端学习1.3 利用JWT生成、验证token;对特定接口配置拦截器_第1张图片

4、配置拦截器

4.1 认证拦截器

package com.example.demo.common;

import cn.hutool.jwt.Claims;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.example.demo.config.MyContext;
import com.example.demo.utils.JwtUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author 
 * @Date 2022/8/1 14:26
 * @Description 拦截器
 */

//认证拦截器
@Component //添加到容器
public class AuthenticationFilter implements HandlerInterceptor {

    @Autowired
    private JwtUtils jwtUtils;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String login = request.getRequestURI();
        if (login.contains("/login")) {
            return true;
        }
        // //获取请求头中的token
        final String token;
        final String authHeader = request.getHeader("Authorization");
        if (StringUtils.isNotBlank(authHeader) && authHeader.startsWith("Bearer ")) {
            //            截取token
            token = authHeader.substring(7);
        } else {
            if (request.getHeader("token") != null) {
                token = request.getHeader("token");
            } else {
                token = request.getParameter("token");
            }
        }
        Map<String, Object> mp = new HashMap<>();
        try {
            if (token == null) {
                System.out.println("token空");
            }
            jwtUtils.verify(token);//验证token
            return true;  //上一句无异常就 放行
        } catch (SignatureVerificationException e) {
//            e.printStackTrace();
            mp.put("msg", "无效签名");
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            mp.put("msg", "token过期,请重新登录");
        } catch (AlgorithmMismatchException e) {
//            e.printStackTrace();
            mp.put("msg", "算法不匹配");
        } catch (NullPointerException e) {
//            e.printStackTrace();
            mp.put("msg", "token不能为空");
        } catch(RuntimeException e){
            e.printStackTrace();
            mp.put("msg", "token不正确");
        } catch (Exception e) {
//            e.printStackTrace();
            mp.put("msg", "其他异常");
        }
        mp.put("state", false);
        //map转换为json
        String json = new ObjectMapper().writeValueAsString(mp);

        response.setContentType("application/json; charset=UTF-8");

        //返回
        response.getWriter().println(json);
        return false;
    }
}

4.2 注入拦截器

package com.example.demo.config;

import com.example.demo.common.AuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Author 
 * @Date 2022/8/1 14:44
 * @Description
 */

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired//必须有这个注解
    private AuthenticationFilter authenticationFilter;

    //    注入拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationFilter)
//       拦截所有请求
//                .addPathPatterns("/**")
                //拦截工作计划接口
                .addPathPatterns("/workPlan/**")
//         排除登录请求
                .excludePathPatterns("/user/login");
    }
}

5、postman设置token方法

后端学习1.3 利用JWT生成、验证token;对特定接口配置拦截器_第2张图片

6、学习到的知识点

  1. token令牌结构:header.payload.signature

  2. 利用JWT生成、解析、验证token

  3. 拦截器配置:①认证拦截器 ②注册拦截器
    //拦截接口 .addPathPatterns(“接口”)
    //排除接口 .excludePathPatterns(“接口”)

  4. postman设置token方法
    way1:加在请求头部
    后端学习1.3 利用JWT生成、验证token;对特定接口配置拦截器_第3张图片
    way2:使之成为全局
    后端学习1.3 利用JWT生成、验证token;对特定接口配置拦截器_第4张图片
    输出上述代码后,执行(send)
    查看:
    后端学习1.3 利用JWT生成、验证token;对特定接口配置拦截器_第5张图片

  5. TokenExpiredException、ExpiredJwtException、RuntimeException

  6. 报错:无法自动装配,找不到’JwtUtils’类型的Bean
    通过添加注解@Repository

  7. 拦截器、过滤器

借鉴的文章部分链接如下:
https://blog.csdn.net/qq_26139541/article/details/110385619
https://www.cnblogs.com/txt1024/p/15730756.html

你可能感兴趣的:(后端开发学习,java,spring,boot,后端,postman)