Java实现登录验证 -- JWT令牌实现

目录

  • 1.实现登录验证的引出
  • 2.JWT令牌
    • [2.1 使用JWT令牌时](#2.1 使用JWT令牌时)
    • [2.2 令牌的组成](#2.2 令牌的组成)
  • [3. JWT令牌(token)生成和校验](#3. JWT令牌(token)生成和校验)
    • [3.1 引入JWT令牌的依赖](#3.1 引入JWT令牌的依赖)
    • [3.2 使用Jar包中提供的API来实现JWT令牌的生成和校验](#3.2 使用Jar包中提供的API来实现JWT令牌的生成和校验)
    • [3.3 使用JWT令牌验证登录](#3.3 使用JWT令牌验证登录)
    • [3.4 令牌的优缺点](#3.4 令牌的优缺点)

1.实现登录验证的引出

传统思路下:

  1. 登录页面把用户名和密码交给服务器。
  2. 服务器验证用户名和密码是否正确,并返回校验结果给后端。
  3. 如果密码正确,就会在服务器创建Session,通过Cookie把sessionId返回给客户端。

原因

但是在集群环境下,无法直接使用Session。因为如果只部署在一台机器时,容易发生单点故障(一旦这台服务器挂了,整个应用就无法访问),所以通常情况下,一个Web应用会部署在多个服务器上,通过Nginx等进行负载均衡。此时,来自一个用户的请求就会分发到不同的服务器上

  • 使用Session时:
    1. 用户登录: 用户登录请求,经过负载均衡发送给服务器1,服务器1进行用户名和密码验证,验证成功后,把Session存在了服务器1上。
    2. 查询操作:用户登录之后,携带Cookie(里面带有SessionId)继续执行查询操作,假如进行查询博客列表,此时请求经过负载均衡发到服务器2上,服务器2会先通过SessionId验证用户是否登录,此时第二台机器上没有该用户的Session,即出现查询不了的问题。

2.JWT令牌

JWT全称:JSON Web Token,用于客户端和服务器之间传递安全可靠的信息,本质是一个token,也叫token,令牌的本质就是一个字符串。相当于现在人们的身份证,出门在外验证身份的时候,拿出身份证即可。

2.1 使用JWT令牌时

  1. 用户登录 : 用户登录请求,经过负载均衡,把请求发给服务器1,服务器1进行账号密码验证,验证成功之后,生成一个令牌,并返回给客户端。
  2. 客户端收到令牌时,把令牌存储起来,可以存储在Cookie中,也可以存储在其它的存储空间,典型的如(localStorage)
  3. 查询操作 用户登录之后,携带令牌继续执行查询操作,假如查询博客列表,此时请求经过负载均衡发到服务器2,服务器2先进行权限验证操作。服务器验证令牌是否有效,如果有效,说明用户已经执行了登录操作,如果无效,说明用户之前未执行登录操作。

2.2 令牌的组成

令牌官网所示,token,本质上一个字符串中间使用 符号 点 . 来分割,令牌由三部分组成,header、payload和verify signature

  • 第一部分:Header(头),令牌的类型和使用的签名算法,如"alg": "HS256(哈希算法)", "typ": "JWT"。

  • 第二部分:Payload(负载),存放一些有效的信息(自定义信息,默认信息)如{"id":"1","username":"zhangsan"},还存在JWT提供的现场字段,如过期时间戳等。

  • 第三部分:Signature(签名),防止token被篡改,确保安全性

    签名的目的就是为了防止token被篡改,而正因为token最后一个部分签名存在,
    所以整个token是非常安全可靠的,一旦token当中的任何一部分被修改,
    整个token在校验的时候都会失败。 
    

3. JWT令牌(token)生成和校验

3.1 引入JWT令牌的依赖

  • 在pom.xml文件中引入依赖
java 复制代码
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt-api</artifactId>
			<version>0.11.5</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt-impl</artifactId>
			<version>0.11.5</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is
preferred -->
			<version>0.11.5</version>
			<scope>runtime</scope>
		</dependency>

3.2 使用Jar包中提供的API来实现JWT令牌的生成和校验

  • 生成token之后,获取token进行解析,创建解释器,设置签名密钥,如果解析token的claims内容不为null,说明校验成功,否则失败。
java 复制代码
package com.example.blog.utils;

import com.example.blog.constant.Constants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.SecretKey;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class JwtUtils {
    // JWT过期时间
    public static final long JWT_EXPIRATION = 60*60*60*1000;
    // 生成key
    private static final String secretStr = "DuJXRS2W3AJHqyFhAplBmsPNawnEdFYFNmlNdMbyU9w=";
    private static final Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretStr));

    /**
     *  生成token
     */
    public static String genJwtToken(Map<String,Object> claim) {
        String token = Jwts.builder().setClaims(claim)
                .setExpiration(new Date(System.currentTimeMillis()+JWT_EXPIRATION))
                .signWith(key)
                .compact();
        return token;
    }


    /**
     *  校验token
     *  Claims 为空,表示jwt校验失败
     *
     */
    public static Claims parseToken(String token) {
        // 创建解析器,设置签名密钥
        JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();
        Claims claims = null;
        try {
            // 解析token
            claims = build.parseClaimsJws(token).getBody();
        }catch (Exception e){
            log.error("解析token失败,token:{}",token);
            return null;
        }
        return claims;
    }
}

3.3 使用JWT令牌验证登录

3.4 令牌的优缺点

  1. 优点:
    • 解决了集群环境下认证的问题
    • 不需要在服务器端存储,从而减轻了服务器的存储压力
  2. 缺点:
    • 需要自己实现令牌的生成、传递、校验
相关推荐
职略35 分钟前
负载均衡类型和算法解析
java·运维·分布式·算法·负载均衡
A227437 分钟前
LeetCode 196, 73, 105
java·算法·leetcode
容若只如初见2 小时前
项目实战--Spring Boot + Minio文件切片上传下载
java·spring boot·后端
阿里巴巴P8资深技术专家2 小时前
Java常用算法&集合扩容机制分析
java·数据结构·算法
weixin_440401692 小时前
分布式锁——基于Redis分布式锁
java·数据库·spring boot·redis·分布式
码农爱java2 小时前
Spring Boot 中的监视器是什么?有什么作用?
java·spring boot·后端·面试·monitor·监视器
zengson_g2 小时前
当需要对大量数据进行排序操作时,怎样优化内存使用和性能?
java·数据库·算法·排序算法
血战灬狂龙3 小时前
pom.xml文件加载后没有变成maven图标
xml·java·maven
2401_858120263 小时前
探索sklearn文本向量化:从词袋到深度学习的转变
开发语言·python·机器学习
无名指的等待7123 小时前
SpringBoot实现图片添加水印(完整)
java·spring boot·后端