基于Springboot实现JWT认证的示例代码

目录
  • 一、了解JWT
    • 概念
    • 作用
    • 1.1 为什么授权要使用jwt
  • 二、JWT结构
    • 2.1 header
    • 2.2 payload
    • 2.3 signature
  • 三、使用JWT
    • 3.1 上手
    • 3.2 封装工具类
    • 3.3 整合springboot

最近一直想写一个类似于待办的东西,由于不想用传统的session,就卡住了,后来在各种群里扯皮,发现除了用缓存之外,还可以通过 JWT 来实现。

一、了解JWT

概念

json web token 用于在各方之间以 json 对象安全地传输信息,比如在前端和后端进行传输,或者在A系统与B系统之间进行传输。因为它是用的数字签名,所以此信息能够进行验证的,验证的成功与否决定是否信任。

作用

  • 授权:这是jwt应用最广泛的。一旦用户登录,每个后续请求都要包含jwt,从而验证用户是否有权限访问资源。单点登录是jwt应用最广泛的一个代表功能。
  • 信息交换:在前后端之间、系统之间,对jwt进行签名,比如使用非对称加密算法(含有公钥和私钥的方式),可以保证信息被指定的人给获取到。

1.1 为什么授权要使用jwt

比较传统session认证与jwt认证的区别

基于传统的session认证

认证方式:http本身是一种是一种无状态的协议。这就意味着,每次进行请求,都要带着用户名和密码来进行用户认证。为了解决这个问题,我们需要在服务端存储一份用户登录的信息,这个登录信息会在响应时传递给客户端,保存成cookie,以便下次携带发送给服务端。这份服务端存储的登录信息就是session。

缺点

  • 每个用户进行认证之后,服务器都要在服务端做一次记录,以便鉴别下次用户请求时的身份。通常而言,session都是保存在内存中,而随着认证用户的增多,服务端的开销会增大。
  • 用户认证之后,服务端在内存中做认证记录,如果下次请求,还需要去访问有记录的服务器才行,这对于分布式的应用来说,体验不好。
  • 基于Cookie识别用户,如果Cookie被抓包到,容易造成跨站请求伪造的攻击。

基于jwt的认证

认证方式:前端携带用户名密码发送到后端接口,后端核对用户名和密码成功后,会将用户的id等信息,作为payload,将其与header分别进行base64加密之后,拼接起来,进行加密,形成一种格式xxx.xxx.xxx的jwt字符串(三部分组成,header、payload、signature,中间用点隔开),返回给前端。前端可以将该信息存储在localStorage或者sessionStorage中,请求时,一般将jwt放入请求头里authorization中。后端会校验jwt是否正确、是否过期,然后拿jwt内部包含的用户信息去进行其他认证通过后的操作。

优点

  • 简洁:jwt可以放在url、请求体、请求头中发送
  • 自包含:payload中包含了用户所需要的所有信息,避免了多次查询数据库
  • 跨语言:jwt是以json的格式保存在客户端的,原则上支持所有形式
  • 分布式:不需要在服务端保存会话信息,特别适合分布式微服务

二、JWT结构

jwt的组成

  • header:标头
  • payload:负载
  • signature:签名

jwt通常如下所示,xxxx.xxxx.xxxx,也就是header.payload.signature

2.1 header

header通常是由两部分组成json。然后进行Base64编码,组成jwt的第一部分

  • 令牌的类型
  • 使用的签名算法,例如HmacSha256、Rsa。jwt推荐使用HmacSha256算法

header中也可以加入一些自定义的内容

例如下面的这种格式

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

jwt为了保证编码的简短,一般会简写过长的单词,如:

  • alg:algorithm,算法
  • typ:type,类型

2.2 payload

payload主要存储用户和其他的一些数据,但是不能存放敏感信息,如密码。然后进行Base64编码,组成jwt的第二部分

payload在java代码里面也叫做claim,即声明。

{
    "userId": "000001",
    "userName": "CCC",
    "admin": 1
}

2.3 signature

header与payload是通过Base64进行编码的,前端是可以解开知道里面的内容的。

signature就是使用header中提供的算法,对经过Base64进行编码过的header和payload,使用我们提供的密钥来进行签名。签名的作用是保证jwt没有被篡改过,如果将signature解密后,与headerBase64.payloadBase64不一致,就是被篡改过的。signature是jwt的第三部分。

如:

String headerPayload=base64UrlEncodeHeader+"."+base64UrlEncodePayload;
String signature=HMACSHA256(headerPayload,secret)

jwt最终的结构,就是将header的base64,payload的base64,signature加密后的值,用.来分割,拼成一串字符串。

三、使用JWT

3.1 上手

引入依赖

<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.18.2</version>
</dependency>

生成jwt

public class GenerateJWT {
    public static void main(String[] args) {
        Map<String,Object> map=new HashMap<>();
        //获取过去时间
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND,10);
        String jwtToken = JWT.create()
                //header,map里面传值,表示在除type、algorithm之外,添加自定义的内容
                .withHeader(map)
                //payload
                .withClaim("userId", "000001")
                .withClaim("userName", "CCC")
                .withClaim("admin", 1)
                //指定过期时间
                .withExpiresAt(calendar.getTime())
                //signature
                .sign(Algorithm.HMAC384("meethigher"));
        System.out.println(jwtToken);
    }
}

校验jwt

public class VerifyJWT {
    public static void main(String[] args) {
        Map<String,Object> map=new HashMap<>();
        //获取过去时间
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND,60);
        String jwtToken = JWT.create()
                //header,map里面传值,表示在除type、algorithm之外,添加自定义的内容
                .withHeader(map)
                //payload
                .withClaim("userId", "000001")
                .withClaim("userName", "CCC")
                .withClaim("admin", 1)
                //指定过期时间
                .withExpiresAt(calendar.getTime())
                //signature
                .sign(Algorithm.HMAC384("meethigher"));
        System.out.println(jwtToken);
        //创建验证对象
        JWTVerifier verifier = JWT.require(Algorithm.HMAC384("meethigher")).build();
        DecodedJWT decodedJWT = verifier.verify(jwtToken);
        System.out.println(decodedJWT.getClaim("userId").asString());
        System.out.println(decodedJWT.getClaim("userName").asString());
        //注意,如果是个整型,用asString会返回一个null。可以点进去查看源码注释
        System.out.println(decodedJWT.getClaim("admin").asInt());
        System.out.println(decodedJWT.getExpiresAt());
    }
}

3.2 封装工具类

JWTUtils.java

public class JWTUtils {
    private static String SECRET = "meethigher";

    /**
     * 传入Payload生成jwt
     *
     * @param map
     * @return
     */
    public static String getToken(Map<String, String> map) {
        Calendar calendar = Calendar.getInstance();
        //7天过期
        calendar.add(Calendar.DAY_OF_MONTH, 7);

        //第一种存payload方式:遍历map存入
//        JWTCreator.Builder builder = JWT.create();
//        map.forEach(builder::withClaim);
        //第二种存payload方式:直接放map,底层采用的也是第一种方式
        return JWT.create()
                .withPayload(map)
                .withExpiresAt(calendar.getTime())
                .sign(Algorithm.HMAC256(SECRET));

    }

    /**
     * 校验签名是否正确,并返回token信息
     *
     * @param token
     * @return
     */
    public static DecodedJWT getTokenInfo(String token) {
        return JWT.require(Algorithm.HMAC256(SECRET))
                .build()
                .verify(token);
    }
}

3.3 整合springboot

关键代码

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 https://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.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>top.meethigher</groupId>
    <artifactId>springboot-jwt</artifactId>
    <version>1.0.0</version>
    <name>springboot-jwt</name>
    <description>chenchuancheng&apos;s demo</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
            <version>3.34.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.github.gwenn/sqlite-dialect -->
        <dependency>
            <groupId>com.github.gwenn</groupId>
            <artifactId>sqlite-dialect</artifactId>
            <version>0.1.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>3.0.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.18.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

#logging:
#  config: classpath:logback.xml
server:
  port: 9090
  ssl:
    enabled: false
spring:
  datasource:
    driver-class-name: org.sqlite.JDBC
    url: jdbc:sqlite:D:/sqliteData/jwt.db
  jpa:
    database-platform: org.sqlite.hibernate.dialect.SQLiteDialect
    hibernate:
      ddl-auto: update
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    show-sql: true
  mvc:
    async:
      request-timeout: 30000

SwaggerConfig

@Configuration
public class SwaggerConfig {
    //配置swagger的实例
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                //只显示包含Api注解的,如果不加这个,会有basic-error-controller显示
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                .paths(PathSelectors.any())
                .build();
    }
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("API接口文档")
                .description("API接口文档")
                .version("1.0")
                .build();
    }
}

InterceptorConfig

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/user/*")
                .excludePathPatterns("/user/login");
    }
}

LoginInterceptor

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        HashMap<String, String> map = new HashMap<>();
        try {
            DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token);
        }catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("desc","无效签名");
        }catch (TokenExpiredException e) {
            e.printStackTrace();
            map.put("desc","token过期");
        }catch (AlgorithmMismatchException e) {
            e.printStackTrace();
            map.put("desc","token算法不一致");
        }catch (Exception e) {
            e.printStackTrace();
            map.put("desc","无效token");
        }
        //如果没有异常,就放行
        if(!ObjectUtils.isEmpty(map)) {
            //转为json,发回给前端
            String json = new ObjectMapper().writeValueAsString(map);
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(json);
            return false;
        }
        return true;
    }
}

参考

JSON Web Token Introduction - jwt.io

Spring Data JPA(二):SpringBoot集成H2_郑龙飞-CSDN博客

到此这篇关于基于Springboot实现JWT认证的文章就介绍到这了,更多相关基于Springboot实现JWT认证内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot使用Jwt处理跨域认证问题的教程详解

    在前后端开发时为什么需要用户认证呢?原因是由于HTTP协定是不存储状态的,这意味着当我们透过账号密码验证一个使用者时,当下一个request请求时他就把刚刚的资料忘记了.于是我们的程序就不知道谁是谁了. 所以为了保证系统的安全,就需要验证用户是否处于登陆状态. 一.JWT的组成 JWT由Header.Payload.Signature三部分组成,分别用.分隔. 下面就是一个jwt真实的样子,说白了就是一个字符串,但是里面却存储了很重要的信息. eyJhbGciOiJIUzI1NiJ9.eyJzd

  • Springboot WebFlux集成Spring Security实现JWT认证的示例

    1 简介 在之前的文章<Springboot集成Spring Security实现JWT认证>讲解了如何在传统的Web项目中整合Spring Security和JWT,今天我们讲解如何在响应式WebFlux项目中整合.二者大体是相同的,主要区别在于Reactive WebFlux与传统Web的区别. 2 项目整合 引入必要的依赖: <dependency> <groupId>org.springframework.boot</groupId> <art

  • springboot+jwt实现token登陆权限认证的实现

    一 前言 此篇文章的内容也是学习不久,终于到周末有时间码一篇文章分享知识追寻者的粉丝们,学完本篇文章,读者将对token类的登陆认证流程有个全面的了解,可以动态搭建自己的登陆认证过程:对小项目而已是个轻量级的认证机制,符合开发需求: 二 jwt实现登陆认证流程 用户使用账号和面发出post请求 服务器接受到请求后使用私钥创建一个jwt,这边会生成token 服务器返回这个jwt给浏览器 浏览器需要将带有token的jwt放入请求头 每次手到客户端请求,服务器验证该jwt的token 验证成功返回

  • SpringBoot集成Spring security JWT实现接口权限认证

    1.添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjw

  • 利用Springboot实现Jwt认证的示例代码

    JSON Web Token是目前最流行的跨域认证解决方案,,适合前后端分离项目通过Restful API进行数据交互时进行身份认证 关于Shiro整合JWT,可以看这里:Springboot实现Shiro+JWT认证 概述 由于概念性内容网上多的是,所以就不详细介绍了 具体可以看这里:阮一峰大佬的博客 我总结几个重点: JWT,全称Json Web Token,是一种令牌认证的方式 长相: 头部:放有签名算法和令牌类型(这个就是JWT) 载荷:你在令牌上附带的信息:比如用户的id,用户的电话号

  • 基于Springboot实现JWT认证的示例代码

    目录 一.了解JWT 概念 作用 1.1 为什么授权要使用jwt 二.JWT结构 2.1 header 2.2 payload 2.3 signature 三.使用JWT 3.1 上手 3.2 封装工具类 3.3 整合springboot 最近一直想写一个类似于待办的东西,由于不想用传统的session,就卡住了,后来在各种群里扯皮,发现除了用缓存之外,还可以通过 JWT 来实现. 一.了解JWT 概念 json web token 用于在各方之间以 json 对象安全地传输信息,比如在前端和后

  • Spring Boot 集成JWT实现前后端认证的示例代码

    目录 前言 JWT简介 为什么要用JWT 传统session认证存在那些弊端? JWT认证的优势 JWT的数据结构 Header Payload Signature Spring Boot集成JWT 引入Jwt包 编写jwt工具类 Token认证拦截器 配置拦击器 登录验证流程 示例代码 总结 前言 小程序.H5应用的快速发展,使得前后端分离已经成为了趋势,然而系统认证却是系统的重要一部分,本文将讲解JWT如何实现前后端认证. JWT简介 JWT(全称:Json Web Token)是一个开放标

  • SpringBoot整合token实现登录认证的示例代码

    1.pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</group

  • SpringBoot整合Sa-Token实现登录认证的示例代码

    目录 依赖 登录 退出登录 前后端分离 今天分享的是 Spring Boot 整合 Sa-Token 实现登录认证. 依赖 首先,我们需要添加依赖: 关键依赖: <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.28.0</version> </depend

  • 在Angular中使用JWT认证方法示例

    本文介绍了在Angular中使用JWT认证方法示例,分享给大家,具体如下: 项目地址: grading-system 基于session的认证和基于token的认证的方式已经被广泛使用.在session认证中,服务端会存储一份用户登录信息,这份登录信息会在响应时传递给浏览器并保存为Cookie,在下次请求时,会带上这份登录信息,这样就能识别请求来自哪个用户. 在基于session的认证中,每个用户都要生成一份session,这份session通常保存在内存中,随着用户量的增加,服务端的开销会增大

  • SpringBoot整合JWT的实现示例

    目录 一. JWT简介 二. Java实现JWT(SpringBoot方式整合) JWT总结 一. JWT简介 1. 什么是JWT? JWT(JSON Web Token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准. 它将用户信息加密到token里,服务器不保存任何用户信息.服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证:应用场景如用户登录.JWT详细讲解请见 github:https://github.com/jwtk/jjwt 2. 为什么使用JWT

  • golang之JWT实现的示例代码

    什么是JSON Web Token? JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON方式安全地传输信息.由于此信息是经过数字签名的,因此可以被验证和信任.可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名. 直白的讲jwt就是一种用户认证(区别于session.cookie)的解决方案. 出现的背景 众所周知,在jwt出现之前,我们已经有session.cookie来解决用户登

  • SpringBoot中实现数据字典的示例代码

    我们在日常的项目开发中,对于数据字典肯定不模糊,它帮助了我们更加方便快捷地进行开发,下面一起来看看在 SpringBoot 中如何实现数据字典功能的 一.简介 1.定义 数据字典是指对数据的数据项.数据结构.数据流.数据存储.处理逻辑等进行定义和描述,其目的是对数据流程图中的各个元素做出详细的说明,使用数据字典为简单的建模项目.简而言之,数据字典是描述数据的信息集合,是对系统中使用的所有数据元素的定义的集合. 数据字典(Data dictionary)是一种用户可以访问的记录数据库和应用程序元数

随机推荐