什么是JWT,SpringBoot整合JWT实现登录认证

java 投稿 1600 0 评论

为什么使用JWT

  1. 简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快。

  2. 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库。

  3. 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。

  4. 不需要在服务端保存会话信息,特别适用于分布式微服务。

JWT的使用场景

身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。

JWT的结构

  • 头部(包含令牌的类型与使用的签名算法)

{"alg": "HS265","typ": "JWT"}

  • 载荷(有关用户实体和其他数据的声明)

{
	"name": "admin",
	"pass": 123
}
  • 签证(使用编码后的header和payload以及一个指定密钥,然后使用header中指定的算法(HS265)进行签名.


  • 签名的作用是保证JWT没有被篡改过)

JWT的请求流程

什么是JWT,SpringBoot整合JWT实现登录认证

SpringBoot整合JWT

引入依赖

    <!--引入jwt-->        <dependency>            <groupId>com.auth0</groupId>            <artifactId>java-jwt</artifactId>            <version>3.4.0</version>        </dependency>        <!--引入mybatis-->        <dependency>            <groupId>org.mybatis.spring.boot</groupId>            <artifactId>mybatis-spring-boot-starter</artifactId>            <version>2.1.3</version>        </dependency>        <!--引入mysql-->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>8.0.15</version>        </dependency> 

编写配置文件

server.port=8989

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://8.192.12.37:3306/test?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root

mybatis.type-aliases-package=com.boot.jwt.entity
mybatis.mapper-locations=com/boot/jwt/mapper/*.xml

logging.level.com.baizhi.dao=debug 

token生成与验证工具类

package com.boot.jwt.config;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.boot.jwt.entity.User;

import java.util.Date;

/**
 * @author laz
 * @date 2022/09/09 14:55
 */
public class TokenUtil {


    //token到期时间60s
    private static final long EXPIRE_TIME= 60*1000;
    //密钥盐
    private static final String TOKEN_SECRET="123456qwertyuiop789";

    /**
     * 创建一个token
     * @param user
     * @return
     */
    public static String sign(User user){
        String token=null;
        try {
            Date expireAt=new Date(System.currentTimeMillis()+EXPIRE_TIME);
            token = JWT.create()
                    //发行人
                    .withIssuer("auth0")
                    //存放数据
                    .withClaim("username",user.getUsername())
                    .withClaim("password",user.getPassword())
                    //过期时间
                    .withExpiresAt(expireAt)
                    .sign(Algorithm.HMAC256(TOKEN_SECRET));
        } catch (IllegalArgumentException|JWTCreationException je) {

        }
        return token;
    }
    /**
     * 对token进行验证
     * @param token
     * @return
     */
    public static Boolean verify(String token){
        try {
            //创建token验证器
            JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
            DecodedJWT decodedJWT=jwtVerifier.verify(token);
            System.out.println("认证通过:");
            System.out.println("username: " + TokenUtil.getUserName(token));
            System.out.println("过期时间:    " + decodedJWT.getExpiresAt());
        } catch (IllegalArgumentException |JWTVerificationException e) {
            //抛出错误即为验证不通过
            return false;
        }
        return true;
    }

    /**
     * 获取用户名
     */
    public static String getUserName(String token){
        try{
            DecodedJWT jwt=JWT.decode(token);
            return  jwt.getClaim("username").asString();
        }catch (JWTDecodeException e)
        {
            return null;
        }
    }
}

拦截器拦截token

package com.boot.jwt.config;

import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author laz
 * @date 2022/09/09 14:56
 */
@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //跨域请求会首先发一个option请求,直接返回正常状态并通过拦截器
        if(request.getMethod().equals("OPTIONS")){
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }

        //获取到token
        String token = request.getHeader("token");
        if (token!=null){
            boolean result= TokenUtil.verify(token);
            if (result){
                System.out.println("通过拦截器");
                return true;
            }
        }
        try {
            JSONObject json=new JSONObject();
            json.put("msg","token verify fail");
            json.put("code","500");
            response.getWriter().append(json.toString());
            System.out.println("认证失败,未通过拦截器");
        } catch (Exception e) {
            return false;
        }
        return false;
    }

}

设置拦截规则

package com.boot.jwt.config;

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.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;

/**
 * @author laz
 * @date 2022/09/09 13:56
 */
@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Autowired
    private TokenInterceptor tokenInterceptor;

    /**
     * 配置拦截器、拦截路径
     * 每次请求到拦截的路径,就会去执行拦截器中的方法
     * @param
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();
        //排除拦截,除了登录,其他都拦截
        excludePath.add("/test/login");
        registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(excludePath);
        WebMvcConfigurer.super.addInterceptors(registry);

    }

}

建立User实体类

package com.boot.jwt.entity;

import lombok.Data;

/**
 * @author laz
 * @date 2022/09/09 14:54
 */
@Data
public class User {
    private String id;
    private String username;
    private String password;
}

 

编写mapper和xml文件

package com.boot.jwt.mapper;

import com.boot.jwt.entity.User;

/**
 * @author laz
 * @date 2022/09/09 14:54
 */
public interface UserMapper {

    /**
     * 登录
     * @param user
     * @return
     */
    User login(User user);
}

  

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.boot.jwt.mapper.UserMapper">

    <select id="login" parameterType="com.boot.jwt.entity.User" resultType="com.boot.jwt.entity.User">
        select * from user where username=#{username} and password = #{password}
    </select>

</mapper> 

编写Service层接口以及实现类

package com.boot.jwt.service;

import com.boot.jwt.entity.User;
import com.boot.jwt.utils.LoginDto;


/**
 * @author laz
 * @date 2022/09/09 14:54
 */
public interface IUserService {

    /**
     * 登录接口
     * @param user
     * @return
     */
    LoginDto login(User user);

}

  

package com.boot.jwt.service.impl;

import com.boot.jwt.config.TokenUtil;
import com.boot.jwt.entity.User;
import com.boot.jwt.mapper.UserMapper;
import com.boot.jwt.service.IUserService;
import com.boot.jwt.utils.LoginDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author laz
 * @date 2022/09/09 14:54
 */
@Service
public class IUserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public LoginDto login(User user) {
        LoginDto loginDto = new LoginDto();
        User login = userMapper.login(user);

        if (login == null){
            loginDto.setCode(400);
            loginDto.setMsg("账号或密码错误!");
            return loginDto;
        }
        String token= TokenUtil.sign(login);

        loginDto.setCode(200);
        loginDto.setMsg("登录成功!");
        loginDto.setUser(login);
        loginDto.setToken(token);
        return loginDto;
    }
}

编写controller层测试接口

package com.boot.jwt.controller;

import com.boot.jwt.entity.User;
import com.boot.jwt.service.IUserService;
import com.boot.jwt.utils.LoginDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author laz
 * @date 2022/09/09 14:58
 */
@RestController
@RequestMapping("/test")
public class LoginController {

    @Autowired
    private IUserService userService;

    /**
     * 登录
     * @param user
     * @return
     */
    @PostMapping("/login")
    public LoginDto login(@RequestBody User user){
        LoginDto login = userService.login(user);
        return login;
    }


    /**
     * 测试
     * @return
     */
    @RequestMapping("/test")
    public Object test(){
        return "访问成功!";
    }

}

测试JWT

先调用测试接口

接下来调用login接口,获取token:

再次调用测试接口,带上token:

可以看到,接口成功访问。

编程学习分享 » 什么是JWT,SpringBoot整合JWT实现登录认证

赞 (0) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽

高效,专业,符合SEO

联系我们