Session 认证和Token 认证

一、基于 Session 认证方式

1.1、http 无状态性

http 是一种无状态协议,就是说每次用户进行用户名和密码认证之后,http 不会留下记录,下一次请求还需要进行认证。因为http 不知道每次请求是哪一个用户发出的。

为了能识别哪个用户提交的请求,在用户首次登录之后,在服务端存储一份用户登录的信息,这份信息在服务端就是通过 session 保存,然后把这份信息返回给客户端,客户端存储在 cookie 中,下次请求时,带上 cookie 中的信息,服务端就可以判断出是哪一个用户的请求。

1.2、session 认证流程

  1. 客户端向服务器发送用户名、密码等认证信息。
  2. 服务器通过验证后,创建一个 session ,并将 session 信息保存起来。
  3. 服务器向客户端返回一个 sessionId ,客户端自动把 sessionId 存储在 cookie 中。
  4. 后续客户端每一次请求。都会在请求头携带 cookie ,将 sessionId 传给服务器。
  5. 服务器获得 cookie 之后,通过 cookie 中的 sessionId 解析出用户信息,执行相关业务。
1.3、存在的问题
  • 每个用户信息都存储在服务端,随着用户量的增加,服务器的开销会增大。
  • session 存储在服务端中,在分布式系统中,这种方式将会失效,为了保证各个服务器中的 session存储的信息一致,需要引入额外的中间件,比如:redis 等
  • 对于非浏览器客户端不适用,原因在于 session 依赖 cookie,移动端没有 cookie
  • 不安全,session 基于 cookie ,如果 cookie 信息被截获,很容易进行 CSRF(跨域请求伪造攻击)

了解到基于 session 认证方式存在的问题之后,有没有一种认证去解决这些问题?下面我们来看看基于Token认证方式。

二、基于 Token 认证方式

token 就是验证用户身份的凭证,成为令牌,

2.1、认证流程

  1. 客户端向服务器发送用户名、密码等认证信息。
  2. 服务端通过验证后,服务端会返回已经签名的 Token,也就是 JWT。
  3. 客户端收到 token 后,存储起来,比如放在 cookie 或者 localStorage 中。
  4. 客户端每次向服务端请求资源时需要带上这个 token。
  5. 服务端收到请求后去验证这个 token,成功则返回请求数据。
2.2、特点:
  • 基于 token 的认证方式是一种服务端无状态的认证方式,服务端不存储 token 数据,适合分布式系统。
  • 不依赖 cookie 机制,适用于浏览器客户端同时也适用移动端。

使用建议

  • 建议将 JWT 存放在 localStorage 中,放在 Cookie 中会有 CSRF 风险。
  • 请求服务端并携带 JWT 的做法是放在 HTTP 请求头中。

三、JWT

3.1、什么是 JWT

概念:JWT 全称 JSON Web Token,是一种基于 Token 的认证授权机制。可以生成 token,也可以解析验证 token。

官网地址https://jwt.io/

先看看官网上 JWT 的具体样式

3.2、JWT 由哪几部分组成

JWT 通常是这样的:xxxxx.yyyyy.zzzzz
示例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

通过示例可以看出 JWT 由 Header、Payload、Signature 三部分组成。

第一部分:Header(头)作用:记录令牌类型、签名算法等。

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

此 JSON 经过 Base64 编码,形成 JWT 的第一部分。

第二部分:Payload(有效载荷)作用:携带一些用户信息或者过期时间

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

Payload 默认是不加密的,不要将隐私数据放到 Payload 中,此 JSON 经过 Base64 编码,形成 JWT 的第二部分。

第三部分:Signature(签名)作用:防止 Token 被篡改,确保安全性。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

Signature 是对前两部分的签名,这个签名生成需要用到:

  • Header + Payload
  • 存放在服务端的密钥(不能泄露)
  • 签名算法

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用(.)分隔,这个字符串就是 JWT 。

3.3、SpringBoot整合JWT

首先新建一个 springBoot 项目 jwt ,然后在 pom 中引入所需依赖。

<?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>

    <groupId>com.duan</groupId>
    <artifactId>jwt</artifactId>
    <version>1.0-SNAPSHOT</version>

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

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

      <!-- JWT依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

       <!-- Springboot测试方法依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>


</project>

在 JWTTest 测试类中新建生成 JWT 方法 getJWT,代码如下

package com.duan;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.Test;

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

/**
 * @author db
 * @version 1.0
 * @description JWTTest
 * @since 2023/12/21
 */
public class JWTTest {

    /**
     * 生成令牌
     */
    @Test
    public void getJWT(){
        Map<String, Object> map = new HashMap<>();
        map.put("id",1);
        map.put("username","程序员康康");
        String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, "cxykk1217")  // 设置签名算法和密钥
                .setClaims(map)   // 设置给定的数据
                .setExpiration(new Date(System.currentTimeMillis() + 1000L * 60 * 3))
                .compact();
        System.out.println(jwt);
    }

}

启动测试方法,打印结果如下:

在 JWTTest 测试类中新建解析 JWT 方法 parseJwt,代码如下:

package com.duan;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.Test;

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

/**
 * @author db
 * @version 1.0
 * @description JWTTest
 * @since 2023/12/21
 */
public class JWTTest {

    /**
     * 解析令牌
     */
    @Test
    public void parseJwt(){
        Claims body = Jwts.parser()
                .setSigningKey("cxykk1217")
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNzAzOTkxMjg5LCJ1c2VybmFtZSI6Iueoi-W6j-WRmOW6t-W6tyJ9.Q_xG0pDaG2rkix2rEw2SNS5uBwTS5f0FbzcLFpQ0yJY")
                .getBody();
        System.out.println(body);
    }
}

启动测试方法,打印结果如下:

以上测试可以生成和解析 JWT,在项目实际使用时 JWT 一般都是以工具类的方式出现。下面我们把生成和解析 JWT 的方法封装成工具类

package com.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

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

/**
 * @author db
 * @version 1.0
 * @description JWTUtils
 * @since 2023/12/31
 */
public class JWTUtils {

    // 密钥
    private static String signKey = "cxykk1217";
    // 过期时间
    private static Long expire = 1000L*60*30; // 30分钟

    /**
     * 生成JWT
     * @param claims JWT第二部分负载payload中存储的内容
     * @return
     */
    public static String generateJwt(Map<String,Object> claims){
        String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, signKey)
                .addClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();

        return jwt;
    }

    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser().setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }

}

代码仓库链接:https://gitee.com/duan138/practice-code/tree/dev/jwt

四、总结

基于 session 和基于 Token 认证方式,本质上没有什么区别,都是对用户身份的认证机制,只是在使用过程中校验的方式不同,各有优缺点,不能说哪个好哪个不好,要根据实际需求选择响应的方式,后续会有文章实现基于 token 的方式去实现用户登录访问控制。


改变你能改变的,接受你不能改变的,关注我,一起成长,共同进步。

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A:在Web开发中,cookie、sessiontoken都用于身份认证和保持用户状态,但它们的实现方式和具体用途不同。 1. cookie(HTTP cookie):是一种在客户端保存数据的机制,当用户访问网站时,服务器可以把需要保存的数据通过Set-Cookie响应头发送给客户端,浏览器会自动把这些数据保存在本地,之后每次请求时会把这些数据通过Cookie请求头发送给服务器。cookie本质上是一组键值对,可以保存一些用户登录状态、偏好设置等信息。 2. session:与cookie类似,也是一种保持用户状态的机制,但它的实现方式不同。session是在服务器端创建的,客户端通过一个session ID与服务端进行交互,服务端维护着session数据。通常情况下,session ID会保存在cookie或者URL参数中。Session通过在服务器端保存用户登录状态可以提高安全性,因为Cookie可能被第三方截获。 3. tokentoken是一种在客户端和服务端之间传递认证信息的机制,一般是由服务端签发的,包含了一些认证信息和有效期等内容。客户端在登录成功后,服务端会返回一个token给客户端,在后续的请求中,客户端需要携带该token才能进行认证token可以放在请求头、Cookie、URL参数等位置,具有跨域、跨平台等优势。相比于使用session,使用token能够将负载更多地分散到客户端。 总结:cookie是在客户端保存数据、session是在服务器端保存会话状态、token是一种通过客户端和服务器之间交互的证明身份机制。而且,它们在功能和使用方式上都存在着不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值