单点登录(SSO)、JWT、HTTP详解及实例演示

单点登录(JWT)

作者:pox21s

1.HTTP

1.1 概述

超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求 - 响应协议,它通常运行在TCP之上。(1)

所谓的"协议"实际上就是双方约定好的"格式"让双方都能看得懂的东西,交互实际上就是"请求"和"响应"。

(1):在HTTP/3 使用的就是 UDP 协议

1.2 发展

HTTP/1.0

HTTP/1.0 成为最重要的面向事务的应用层协议。该协议对每一次请求/响应建立并拆除一次连接。默认是短连接,每次客户端和服务器的交互都要建立一次连接。

HTTP/1.1

HTTP1.1 版本最主要的是「默认持久连接」。只要客户端服务端没有断开 TCP 连接,就一直保持连接,可以发送多次 HTTP 请求。

其次就是「断点续传」(Chunked transfer-coding) 利用 HTTP 消息头使用分块传输编码,将实体主体分块进行传输。

HTTP/2

HTTP/2 不再以文本的方式传输,采用二进制分帧层,对头部进行了「压缩] 支持「流控」最主要就是HTTP/2是支持「多路复用」的 (通过单一的TCP连接 并行 发起多个的请求和响应消息)

补充:

HTTP/2多路复用则是利用「分帧」数据流,把HTTP协议分解为「互不依赖」的帧 (为每个帧 「标序」发送,接收回来的时候按序重组),进而可以「乱序」发送避免一定程度上的队首阻塞问题。

但是,无论是 HTTP/1.1 还是 HTTP/2,response 响应的「处理顺序」总是需要跟 request 请求顺序保持一致的。假如某个请求的 response 响应慢了,还是同样会有阻塞的问题。

受限于底层的 TCP 连接,没办法完全解决 线头阻塞 问题

HTTP/3

HTTP/3 跟前面版本最大的区别就是:HTTP1.x 和 HTTP/2 底层都是 TCP,而 HTTP/3 底层是 UDP,使用 HTTP/3 能够减少 RTT 往返时延 (TCP三次握手,TLS握手)

2.HTTPS

2.1 概述

HTTPS (全称:Hyper Text Transfer Protocol over SecureSocket Layer),是以安全为目标的 HTTP 通道,在 HTTP 的基础上通过传输加密和身份认证保证了传输过程的安全性。HTTPS 在 HTTP 的基础下加入 SSL,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL。 HTTPS 存在不同于 HTTP 的默认端口及一个加密/身份验证层(在 HTTP与 TCP 之间)。这个系统提供了身份验证与加密通讯方法。

安全升级的 HTTP

2.2 原理、过程

  1. 客户端将它所支持的算法列表和一个用作产生密钥的随机数发送给服务器

  2. 服务器从算法列表中选择一种加密算法,并将它和一份包含服务器公用密钥的证书发送给客户端;该证书还包含了用于认证目的的服务器标识,服务器同时还提供了一个用作产生密钥的随机数(1)
    在这里插入图片描述

  3. 客户端对服务器的证书进行验证(有关验证证书,可以参考数字签名),并抽取服务器的公用密钥;然后,再产生一个称作 pre_master_secret 的随机密码串,并使用服务器的公用密钥对其进行加密(参考非对称加 / 解密),并将加密后的信息发送给服务器

  4. 客户端与服务器端根据 pre_master_secret 以及客户端与服务器的随机数值独立计算出加密和 MAC密钥(参考 DH密钥交换算法)

  5. 客户端将所有握手消息的 MAC 值发送给服务器

  6. 服务器将所有握手消息的 MAC 值发送给客户端

(1):证书的发放一般由 CA(公信机构)扮演,证书通常会内置在浏览器或操作系统中

3.单点登录

3.1 Session、Cookie 服务器验证模式

3.1.1 过程

  1. 用户向服务器发送用户名和密码
  2. 验证服务器后,相关数据(如用户名,用户角色等)将保存在当前会话(session)中
  3. 服务器向用户返回 session_id,session 信息都会写入到用户的Cookie
  4. 用户的每个后续请求都将通过在 Cookie 中取出 session_id 传给服务器
  5. 服务器收到 session_id 并对比之前保存的数据,确认用户的身份

3.1.2 缺点

  • 单点性能压力,无法扩展
  • 分布式架构中,需要 Session 共享方案,Session 共享方案存在性能瓶颈
  • 每次验证都需要进行一次服务器查询
  • 在服务器中 Session 都是保存在内存中,访问量大,很容易占满内存
  • 面对如今的分布式,每一台服务器都需要保存所有访问的 Session

3.2 单点登录(SSO)模式概述

分布式,SSO(single sign on)模式:单点登录英文全称 Single Sign On,简称就是SSO。

它的解释是:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统并且不需要在服务器中进行验证查询。

将登录抽取出来成为一个单独的模块,为所有其他的服务模块工作,其他模块无需用于登录系统,减少业务模块的压力。

3.3 过程

  1. 当业务 A、业务 B 需要登录时,将跳到SSO系统
  2. SSO 从用户信息数据库中获取用户信息并校验用户信息,SSO 系统完成登录
  3. 然后将用户信息存入缓存(例如redis)
  4. 当用户访问业务 A 或业务 B,需要判断用户是否登录时,将跳转到 SSO 系统中进行用户身份验证,SSO 判断缓存中是否存在用户身份信息
  5. 这样,只要其中一个系统完成登录,其他的应用系统也就随之登录了。这就是单点登录(SSO)的定义

3.4 优缺点

1.优点

用户身份信息独立管理,更好的分布式管理。可以自己扩展安全策略。

2.缺点

认证服务器访问压力较大

4.Token(令牌)

4.1 概述

通过发放令牌的模式,可以设置令牌的过期时间等,通过令牌,在规定时间内,可以在多个服务门模块中进行访问和跳转,而无需多次令牌放发。

4.2 令牌的类型

JWT 是一种令牌用来辅助 单点登录 的实现,下面详细介绍 JWT

4.3 Token 的优点

  • 支持跨域访问: Cookie 是不允许垮域访问的,这一点对 Token 机制是不存在的,前提是传输的用户认证信息通过 HTTP 头传输

    Token 在 HTTP 头中只需验证头部信息即可

  • 无状态(也称:服务端可扩展行):Token 机制在服务端不需要存储 session 信息,因为 Token 自身包含了所有登录用户的信息,只需要在客户端的 cookie 或本地介质存储状态信息

  • 去耦: 不需要绑定到一个特定的身份验证方案。Token 可以在任何地方生成,只要在你的 API 被调用的时候,你可以进行 Token 生成调用即可。

  • 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie 是不被支持的(你需要通过Cookie容器进行处理),这时采用 Token 认证机制就会简单得多。

  • CSRF(跨域):因为不再依赖于 Cookie,所以你就不需要考虑对 CSRF(跨站请求伪造)的防范

  • 性能: 一次网络往返时间(通过数据库查询session信息)总比做一次 HMACSHA256 计算的 Token 验证和解析要费时得多

  • 因为 json 的通用性,所以 JWT 是可以进行跨语言支持的,像 JAVA、JavaScript、NodeJS、PHP 等很多语言都可以使用

  • 因为有了有效负载部分,所以 JWT 可以在自身存储一些其他业务逻辑所必要的非敏感信息

  • 便于传输,jwt 的构成非常简单,字节占用很小,所以它是非常便于传输的

  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展

5.JWT

5.1 概述

JWT是JSON Web Token 的缩写,即 JSON Web令 牌,是一种自包含令牌。

JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。

客户端接收服务器返回的 JWT,将其存储在 Cookie 或 localStorage 中。

此后,客户端将在与服务器交互中都会带 JWT。如果将它存储在 Cookie 中,就可以自动发送,但是不会跨域,因此一般是将它放入 HTTP 请求的 Header Authorization 字段中。

当跨域时,也可以将 JWT 放置于 POST 请求的数据主体中。(1)

(1):关于跨域,推荐文章:https://blog.csdn.net/qq_38128179/article/details/84956552

5.2 作用及原理

1.作用

JWT 最重要的作用就是对 token 信息的防伪作用。

2.原理
  • 一个 JWT 由三个部分组成:JWT头、有效载荷、签名哈希
  • 最后由这三者组合进行base64编码得到 JWT

官网:https://jwt.io/,可以查看 JWT 的组成:

该对象为一个很长的字符串,字符之间通过"."分隔符分为三个子串

每一个子串表示了一个功能块,总共有以下三个部分:JWT头、有效载荷和签名

5.3 组成

5.3.1 JWT 头

JWT 头部分是一个描述 JWT 元数据的 JSON 对象,通常如下所示:

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

在上面的代码中,alg 属性表示签名使用的算法,默认为 HMAC SHA256(写为HS256);

typ 属性表示令牌的类型,JWT 令牌统一写为 JWT。最后,使用 Base64 URL 算法将上述 JSON 对象转换为字符串保存。

5.3.2 有效载荷

有效载荷部分,是 JWT 的主体内容部分,也是一个 JSON 对象,包含需要传递的数据。 JWT 指定七个默认字段供选择:

  • iss: jwt 签发者
  • sub: 主题
  • aud: 接收 jwt 的一方
  • exp: jwt 的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该 jwt 都是不可用的
  • iat: jwt 的签发时间
  • jti: jwt 的唯一身份标识,主要用来作为一次性 token,从而回避重放攻击

除以上默认字段外,我们还可以自定义私有字段,如下例:

{
  "name": "pox21s",
  "admin": true,
  "avatar": "pox21s.jpg"
}

请注意,默认情况下 JWT 是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。

JSON 对象也使用 Base64 URL 算法转换为字符串保存。

**注意:**base64 编码,并不是加密,只是把明文信息变成了不可见的字符串。但是其实只要用一些工具就可以把base64 编码解成明文,所以不要在 JWT 中放入涉及私密的信息。

5.3.3 签名哈希

签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。

首先,需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。

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

在计算出签名哈希后,JWT 头、有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT 对象。

5.4 过程

  • 用户使用用户名密码来请求服务器
  • 服务器进行验证用户的信息
  • 服务器通过验证发送给用户一个 token
  • 客户端存储 token,并在每次请求时附送上这个 token 值
  • 服务端验证 token 值,并返回数据(1)

(1):验证的方式是,对发来 token 进行再次的加密,然后验证是否和 token 中已经存在的秘钥相同,如果相同,那么就认为是允许访问,通过加密算法以后,认为不同的头部以及载荷内容进行签名的结果是一定不同的。

通过这种方式就不用进行数据库用户端验证,只需在第一次登录时验证,在有效的时间内减少数据库查询的 IO 次数。

这个 token 必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持 CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了 Access-Control-Allow-Origin:*

5.4 总结

  1. JWT 默认不加密,但可以加密。生成原始令牌后,可以使用该令牌再次对其进行加密

  2. 当 JW T未加密时,一些私密数据无法通过 JWT 传输

  3. JWT 不仅可用于认证,还可用于信息交换。善用 JWT 有助于减少服务器请求数据库的次数

  4. JWT 的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT 签发,在有效期内将会一直有效

  5. JWT 本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT 的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行身份验证

  6. 为了减少盗用和窃取,JWT 不建议使用 HTTP 协议来传输代码,而是使用加密的 HTTPS 协议进行传输

6.应用实例

  • Spring Boot 2.2.2.RELEASE
  • Maven 3.8
  • JDK 8

6.1 概述

  • 导入依赖
  • 创建验证对象信息封装类
  • 创建 JWT 工具类
  • 创建测试类

6.2 详细

1.导入依赖
<dependencies>
    <!-- JWT -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.7.0</version>
    </dependency>

    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.6</version>
    </dependency>
    
     <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>
2.创建验证对象信息封装类

将前端传入的数据封装到一个对象中,便于数据的获取。

// lombok 简化代码编写
@Data
public class Member {
	 // 用户 id
    private String id;
    // 用户昵称
    private String nickname;
    // 用户头像
    private String avatar;
}
3.创建 JWT 工具类

编写 JWT 工具类,用户令牌的发放和令牌的验证。

public class JwtUtils {

    public static final String SUBJECT = "guli-user";

    // 自定义秘钥,用于签名加密
    public static final String APP_SECRET = "79e7c69681b8270162386e6daa53d1dc";

    // 设置过期时间,毫秒,30分钟
    public static final long EXPIRE = 1000 * 60 * 30;

    /**
     * 生成Jwt令牌
     * @return
     */
    public static String generateJwt(Member member){

        String token = Jwts.builder()
           		 // 令牌类型
                .setHeaderParam("typ", "JWT") 
                // 签名算法
                .setHeaderParam("alg", "HS256") 
            	 // 令牌主题
                .setSubject(SUBJECT) 
           		 // 签发时间
                .setIssuedAt(new Date()) 
          		 // 过期时间
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) 
           		 // 负载部分
                .claim("id", member.getId())
                .claim("nickname", member.getNickname())
                .claim("avatar", member.getAvatar())
                // 签名加密
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
           		 .compact();

        return token;
    }


    /**
     * 校验jwt
     * @param jwtToken
     * @return
     */
    public static Claims checkJwt(String jwtToken){
		  // 加密验证
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
       
        Claims claims = claimsJws.getBody();

        return claims;
    }
}
4.创建测试类

结果测试。

public class JwtTest {

    @Test
    public void testGenerateJwt(){
        // 对象数据封装
        Member member = new Member();
        member.setId("10000");
        member.setNickname("pox21s");
        member.setAvatar("1.png");
        // 生成 JWT 令牌
        String jwt = JwtUtils.generateJwt(member.getId(), member.getNickname(), member.getAvatar());
       
        System.out.println(jwt);
    }

   @Test
    public void testCheckJwt(){

        Claims claims = JwtUtils.checkJwt("jwt字符串");
			
        // 数据验证
        String id = (String)claims.get("id");
        String nickname = (String)claims.get("nickname");
        String avatar = (String)claims.get("avatar");

        System.out.println(id);
        System.out.println(nickname);
        System.out.println(avatar);
    }
}
  • 0
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pox21s

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值