springboot整合JWT

有关JWT的详解可上网查资料,其主要就是用来生成token,举个例子,访问接口需要先进行登录认证,认证通过才能进行访问。也就是先通过用户名和密码登录,后台查询是否存在该用户,若存在则生成token返回,当访问其他接口时携带token请求,后台验证token,验证成功则可以访问,验证失败则禁止访问。

下面为使用JWT生成token以及验证token的简单测试代码:

生成token:

 @Test
    public void jwtTest() {
        //设置从现在开始10秒后
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,90);
        //生成令牌
        String token = JWT.create()
                .withClaim("username","张三") //设置自定义用户名
                .withExpiresAt(instance.getTime())  //设置过期时间
                .sign(Algorithm.HMAC256("token*!us%#Eu"));  //设置签名 尽量复杂
        //输出令牌
        System.out.println(token);

运行结果:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2Mzc3Mzg0NTksInVzZXJuYW1lIjoi5byg5LiJIn0.yjomlApRc9lVb8CI0Nn6kh5GUCeCHfDQDZOvBn6e31A

验证token:

@Test
    public void JwtVerifyTest() {
        //生成token时所使用的签名
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("token*!us%#Eu")).build();
        //验证生成的token
        DecodedJWT decodedJWT = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2Mzc3Mzg0NTksInVzZXJuYW1lIjoi5byg5LiJIn0.yjomlApRc9lVb8CI0Nn6kh5GUCeCHfDQDZOvBn6e31A");
        System.out.println("用户名:" + decodedJWT.getClaim("username").asString());//存的什么类型取就用什么类型
        System.out.println("过期时间:" + decodedJWT.getExpiresAt());
    }

运行结果:

用户名:张三
过期时间:Wed Nov 24 15:20:59 CST 2021
======================================================================

======================================================================

下面使用Springboot+JWT来实现用户想要访问某些业务接口需要先登录、获取token、验证token,验证通过才可以访问。

工程目录:

pom.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>org.example</groupId>
    <artifactId>java_test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--引入mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <!--引入jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.10.3</version>
        </dependency>

        <!--引入mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>

        <!--引入druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.23</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>



</project>

resource下的application.properties配置文件:

server.port=8989
server.servlet.context-path=/jwt_demo

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/pcprtest?serverTimezone=Asia/Shanghai&useSSL=true
spring.datasource.username=root
spring.datasource.password=12345

mybatis.type-aliases-package=com.test.entity
mybatis.mapper-locations=classpath:mapper/*.xml

logging.level.com.test.dao=debug

 由于本次针对实现token获取及验证流程,所涉及的代码十分简单,在此省略entity、dao、mapper的代码。

JWT工具类:

package com.test.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Calendar;
import java.util.Map;

public class JWTUtils {

    private static String TOKEN = "token*!us%#Eu";

    /**
     * 生成token
     * @param map
     * @return
     */
    public static String getToken(Map<String,String> map) {
        JWTCreator.Builder builder = JWT.create();
        //这里采用lambda表达式
        //遍历map,设置负载(payload)
        map.forEach((k,v) -> builder.withClaim(k,v));
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,90);
        builder.withExpiresAt(instance.getTime());
        return builder.sign(Algorithm.HMAC256(TOKEN));
    }

    /**
     * 验证token
     * @param token
     */
    public static void verify(String token) {
        JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token);
    }

    /**
     * 获取token中的负载信息(payload)
     * @param token
     * @return
     */
    public static DecodedJWT getTokenInfo(String token) {
        return JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token);
    }
}

UserService接口:

package com.test.service;

import com.test.entity.UserEntity;

public interface UserService {
    UserEntity login(UserEntity user);
}

 UserServiceImpl实现类:

package com.test.service.impl;

import com.test.dao.UserDao;
import com.test.entity.UserEntity;
import com.test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public UserEntity login(UserEntity user) {
        //根据接收的用户名和密码查询数据库
        UserEntity userEntity = userDao.login(user);
        if (userEntity != null) {
            return userEntity;
        }
        throw new RuntimeException("登录失败--");
    }
}

ControllerTest类:

package com.test.controller;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.test.entity.UserEntity;
import com.test.service.UserService;
import com.test.utils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
@PostMapping("/user")
@Slf4j
public class ControllerTest {

    @Autowired
    UserService userService;

    @RequestMapping("/login")
    public Map<String,Object> login(@RequestBody UserEntity user) {
        Map<String,Object> result = new HashMap<>();
        log.info("用户名:[{}]",user.getUsername());
        log.info("密码:[{}]",user.getPassword());
        try {
            UserEntity userEntity = userService.login(user);
            Map<String,String> payloadMap = new HashMap<>();
            payloadMap.put("id",userEntity.getId().toString());
            payloadMap.put("username",userEntity.getUsername());
            String token = JWTUtils.getToken(payloadMap);
            result.put("state",true);
            result.put("msg","登陆成功~");
            result.put("token",token);
        } catch (Exception e) {
            result.put("state",false);
            result.put("msg",e.getMessage());
        }
        return result;
    }

    @PostMapping("/verify")
    public Map<String,Object> verify(@RequestParam("token") String token) {
        Map<String,Object> map = new HashMap<>();
        try {
            JWTUtils.verify(token);
            map.put("state",true);
            map.put("msg","验证通过~~");
        } catch (TokenExpiredException e) {
            map.put("state",false);
            map.put("msg","token已过期!");
        } catch (SignatureVerificationException e) {
            map.put("state",false);
            map.put("msg","签名错误!");
        } catch (AlgorithmMismatchException e) {
            map.put("state",false);
            map.put("msg","算法不匹配!");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("state",false);
            map.put("msg","无效token!");
        }
        return map;
    }

}

Postman调用登录接口进行测试:

输入错误的用户名和密码:

输入正确的用户名和密码:

下面来测试token验证接口:

token过期

错误的token

有效的token

 那么又有问题了,如果这么设计,每个接口都需要加上token验证流程,产生代码冗余。因此我们采用拦截器来实现需求。

 自定义拦截器JWTInterceptor类:

package com.test.interceptor;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.utils.JWTUtils;
import org.springframework.web.servlet.HandlerInterceptor;

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

public class JWTIntercepter implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        Map<String,Object> map = new HashMap<>();
        try {
            JWTUtils.verify(token);
            map.put("state",true);
            map.put("msg","验证通过~~");
            return true;
        } catch (TokenExpiredException e) {
            map.put("state",false);
            map.put("msg","token已过期!");
        } catch (SignatureVerificationException e) {
            map.put("state",false);
            map.put("msg","签名错误!");
        } catch (AlgorithmMismatchException e) {
            map.put("state",false);
            map.put("msg","算法不匹配!");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("state",false);
            map.put("msg","无效token!");
        }
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        return false;
    }
}

 

 拦截器配置类InterceptorConfig:

package com.test.config;

import com.test.interceptor.JWTIntercepter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTIntercepter())
                .excludePathPatterns("/user/**")    //  放行/User/**的请求路径
                .addPathPatterns("/**"); //  拦截除了/user/**的所有请求路径
    }
}

添加其他的业务Controller类:

package com.test.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
@RequestMapping("/business")
public class BusinessController {

    @RequestMapping("/test1")
    public Map<String,Object> businessOne() {
        Map<String,Object> map = new HashMap<>();
        map.put("msg","业务接口1");
        map.put("state",true);
        return map;
    }
    @RequestMapping("/test2")
    public Map<String,Object> businessTwo() {
        Map<String,Object> map = new HashMap<>();
        map.put("msg","业务接口2");
        map.put("state",true);
        return map;
    }
    @RequestMapping("/test3")
    public Map<String,Object> businessThree() {
        Map<String,Object> map = new HashMap<>();
        map.put("msg","业务接口3");
        map.put("state",true);
        return map;
    }
}

 

下面来进行测试:

不登录直接调用业务接口

登录后获取token,之后将token放入请求头中调用业务接口

 至此,Springboot+JWT实现token获取验证的流程就实现了~~~

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值