亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

JWT單點登錄學習入門:從零開始的詳細教程

標簽:
安全 API
概述

本文将详细介绍JWT的基础概念、单点登录的优势及其实现方式,以及如何使用JWT实现单点登录的具体步骤和示例代码。JWT单点登录学习入门包括创建和验证JWT令牌、访问不同应用的资源以及安全性考虑。

JWT基础概念

什么是JWT

JSON Web Token (JWT) 是一种开放标准(RFC 7519),用于在各方之间安全地将信息作为JSON对象传输。JWT是一种紧凑、自包含的方式,用来在身份验证和授权信息之间传递数据。JWT由三部分组成,它们之间用点(.)分隔,这三部分依次是头部(Header)、载荷(Payload)和签名(Signature)。

JWT的工作原理

JWT的工作原理如下:

  1. 用户通过身份验证(例如提供用户名和密码)。
  2. 服务端验证用户身份后,生成一个JWT。
  3. 服务端将JWT返回给客户端。
  4. 客户端将JWT包含在HTTP请求头中的Authorization字段,或者作为URL参数、Cookie等发送给服务器。
  5. 服务端接收到JWT,验证其有效性并进行授权。

JWT的组成部分

JWT由三个部分组成,分别用英文大写表示:HEADERPAYLOADSIGNATURE

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

    上面的JSON对象指定了JWT所使用的算法(alg)和类型(typ)。

  • PAYLOAD
    载荷是JWT的主体部分,包含了JWT的声明。
    {
    "sub": "1234567890",
    "name": "John Doe",
    "iat": 1516239022
    }

    上面的JSON对象包含了主体(sub)、姓名(name)以及签发时间(iat)等声明。

  • SIGNATURE
    签名保证了JWT的完整性和可靠性。签名由头部、载荷和密钥生成。

    import hmac
    import base64
    import json
    
    def create_signature(header, payload, secret):
      encoded_header = base64.urlsafe_b64encode(json.dumps(header).encode()).rstrip(b'=')
      encoded_payload = base64.urlsafe_b64encode(json.dumps(payload).encode()).rstrip(b'=')
      message = encoded_header + b"." + encoded_payload
      signature = hmac.new(secret, message, digestmod='SHA256').digest()
      return base64.urlsafe_b64encode(signature).rstrip(b'=')
    
    secret = b'secret_key'
    header = {"alg": "HS256", "typ": "JWT"}
    payload = {"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
    
    signature = create_signature(header, payload, secret)
    print(signature)
单点登录(SSO)基础

什么是单点登录

单点登录(Single Sign-On,SSO)是一种身份验证方法,允许用户通过一次登录操作,访问多个相关的、相互信任的应用程序或网站。这种方式简化了用户身份验证的过程,提高了用户体验。

单点登录的优势

  • 用户体验提升:用户只需要一次登录即可访问多个系统,减少在不同系统间切换登录的麻烦。
  • 统一管理:统一管理用户身份信息,简化了身份验证和授权的复杂度。
  • 减少安全风险:单点登录机制可以集中管理和监控用户访问情况,减少因多次登录而出现的安全隐患。

SSO的实现方式

SSO的实现方式主要有以下几种:

  • 基于Cookie的SSO
    利用浏览器的Cookie机制实现单点登录。
  • 基于Token的SSO
    使用Token(如JWT)实现单点登录。
  • 基于OAuth的SSO
    使用OAuth协议实现单点登录。
实现JWT单点登录

创建JWT令牌

创建JWT令牌需要以下几个步骤:

  1. 定义JWT头部和载荷。
  2. 生成签名。
  3. 将头部、载荷和签名组合成最终的JWT令牌。

以下是一个使用Python示例代码来创建JWT令牌:

import hmac
import base64
import json
import time

def create_jwt(header, payload, secret):
    encoded_header = base64.urlsafe_b64encode(json.dumps(header).encode()).rstrip(b'=')
    encoded_payload = base64.urlsafe_b64encode(json.dumps(payload).encode()).rstrip(b'=')
    message = encoded_header + b"." + encoded_payload
    signature = hmac.new(secret, message, digestmod='SHA256').digest()
    encoded_signature = base64.urlsafe_b64encode(signature).rstrip(b'=')
    jwt = encoded_header.decode() + '.' + encoded_payload.decode() + '.' + encoded_signature.decode()
    return jwt

header = {"alg": "HS256", "typ": "JWT"}
payload = {"sub": "1234567890", "name": "John Doe", "iat": int(time.time())}
secret = b'secret_key'

jwt = create_jwt(header, payload, secret)
print(jwt)

验证JWT令牌

验证JWT令牌需要以下几个步骤:

  1. 解析JWT的头部、载荷和签名。
  2. 根据头部信息选择合适的算法,使用提供的密钥生成新的签名。
  3. 比较生成的签名和JWT中的签名,如果一致则表示JWT有效。

以下是一个使用Python示例代码来验证JWT令牌:

import hmac
import base64
import json
import time

def validate_jwt(jwt, secret):
    parts = jwt.split('.')
    encoded_header = parts[0]
    encoded_payload = parts[1]
    encoded_signature = parts[2]

    header = json.loads(base64.urlsafe_b64decode(encoded_header + '=='))
    payload = json.loads(base64.urlsafe_b64decode(encoded_payload + '=='))

    message = encoded_header + b"." + encoded_payload
    new_signature = hmac.new(secret, message, digestmod='SHA256').digest()
    encoded_new_signature = base64.urlsafe_b64encode(new_signature).rstrip(b'=')

    if encoded_new_signature.decode() == encoded_signature.decode():
        return True
    return False

secret = b'secret_key'
jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogIkpvaG4gRG9lIiwgImlhdCI6IDE1MTYyMzkwMjJ9.01J53P4V7t2qUJ8NcMn30sUfFyT1LzIaO9iB7kRQKc'

is_valid = validate_jwt(jwt, secret)
print(is_valid)

访问不同应用的资源

访问不同应用的资源需要以下步骤:

  1. 生成JWT令牌,并在请求头中包含该令牌。
  2. 服务端验证JWT令牌的有效性并进行授权。
  3. 根据令牌中的信息,服务端返回相应的资源。

示例代码(使用Python模拟HTTP请求,发送JWT令牌):

import requests
import base64

jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogIkpvaG4gRG9lIiwgImlhdCI6IDE1MTYyMzkwMjJ9.01J53P4V7t2qUJ8NcMn30sUfFyT1LzIaO9iB7kRQKc'

headers = {
    'Authorization': f'Bearer {jwt}',
    'Content-Type': 'application/json'
}

response = requests.get('http://example.com/api/resource', headers=headers)

print(response.json())
JWT单点登录实战

使用Spring Boot实现JWT单点登录

Spring Boot可以方便地集成JWT实现单点登录功能。下面是一个简单的示例:

依赖配置

pom.xml中添加JWT依赖:

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.16.0</version>
</dependency>

创建JWT工具类

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.security.Key;

public class JwtUtils {
    public static String createToken(ObjectNode payload, String secret) throws Exception {
        Algorithm algorithm = Algorithm.HMAC256(secret);
        return JWT.create()
                .withPayload(payload.toString())
                .sign(algorithm);
    }

    public static DecodedJWT verifyToken(String token, String secret) {
        Algorithm algorithm = Algorithm.HMAC256(secret);
        return JWT.require(algorithm)
                .build()
                .verify(token);
    }
}

创建登录控制器

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.web.bind.annotation.*;

@RestController
public class LoginController {
    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) throws Exception {
        // 模拟用户认证逻辑
        if ("admin".equals(username) && "password".equals(password)) {
            ObjectMapper mapper = new ObjectMapper();
            ObjectNode payload = mapper.createObjectNode();
            payload.put("username", "admin");
            payload.put("iat", System.currentTimeMillis() / 1000L);
            return JwtUtils.createToken(payload, "secret_key");
        }
        return null;
    }

    @GetMapping("/protected-resource")
    public String protectedResource(@RequestHeader("Authorization") String authorization) {
        String token = authorization.replace("Bearer ", "");
        DecodedJWT decodedJWT = JwtUtils.verifyToken(token, "secret_key");
        if (decodedJWT != null) {
            return "Access granted!";
        }
        return "Access denied!";
    }
}

使用Node.js实现JWT单点登录

Node.js可以使用npm库实现JWT单点登录功能。下面是一个简单的示例:

安装依赖

npm install jsonwebtoken express

创建JWT工具类

const jwt = require('jsonwebtoken');

function createToken(payload, secret) {
    return jwt.sign(payload, secret, {algorithm: 'HS256'});
}

function verifyToken(token, secret) {
    return jwt.verify(token, secret);
}

创建Express应用

const express = require('express');
const app = express();
const jwt = require('jsonwebtoken');

app.use(express.json());

app.post('/login', (req, res) => {
    const { username, password } = req.body;
    // 模拟用户认证逻辑
    if (username === 'admin' && password === 'password') {
        const payload = { username: 'admin', iat: Math.floor(Date.now() / 1000) };
        const token = createToken(payload, 'secret_key');
        res.json({ token });
    } else {
        res.status = 401;
        res.json({ error: 'Unauthorized' });
    }
});

app.get('/protected-resource', (req, res) => {
    const token = req.headers.authorization?.replace('Bearer ', '');
    if (token) {
        const decoded = verifyToken(token, 'secret_key');
        if (decoded) {
            res.json({ message: 'Access granted!' });
        } else {
            res.status = 401;
            res.json({ error: 'Unauthorized' });
        }
    } else {
        res.status = 401;
        res.json({ error: 'Unauthorized' });
    }
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});
安全性考虑

JWT的安全威胁

  • 密钥泄露:如果JWT使用的密钥被泄露,攻击者可以伪造JWT。
  • Token被拦截:JWT无状态,一旦Token被拦截并存储,攻击者可以使用它来冒充用户。
  • Token过期时间过长:过长的过期时间可能导致安全性问题。

如何提高JWT安全性

  • 使用强密钥:确保JWT的密钥足够复杂且不易被猜测。
  • 限制Token过期时间:设置合理的过期时间,避免Token过长的有效期。
  • 使用HTTPS:确保所有的JWT传输都在HTTPS下进行,以防止中间人攻击。
  • Token刷新机制:实现Token刷新机制,确保每次操作后都刷新Token。
  • 分离JWT的头部和载荷:确保头部和载荷的正确性和完整性。
  • 限制Token的使用范围:仅在必要时生成和使用JWT。

示例代码展示如何处理JWT的安全威胁和提高安全性

使用强密钥

import hmac
import base64
import json

def create_secure_jwt(header, payload, secret):
    encoded_header = base64.urlsafe_b64encode(json.dumps(header).encode()).rstrip(b'=')
    encoded_payload = base64.urlsafe_b64encode(json.dumps(payload).encode()).rstrip(b'=')
    message = encoded_header + b"." + encoded_payload
    signature = hmac.new(secret, message, digestmod='SHA256').digest()
    encoded_signature = base64.urlsafe_b64encode(signature).rstrip(b'=')
    jwt = encoded_header.decode() + '.' + encoded_payload.decode() + '.' + encoded_signature.decode()
    return jwt

header = {"alg": "HS256", "typ": "JWT"}
payload = {"sub": "1234567890", "name": "John Doe", "iat": int(time.time())}
secret = b'385583674a8f5a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8367485a8'
jwt = create_secure_jwt(header, payload, secret)
print(jwt)

# 示例:限制Token过期时间
def create_jwt_with_expiry(header, payload, secret, expiry):
    payload['exp'] = int(time.time()) + expiry
    jwt = create_jwt(header, payload, secret)
    return jwt

expiry = 3600  # Token过期时间设定为1小时
jwt_with_expiry = create_jwt_with_expiry(header, payload, secret, expiry)
print(jwt_with_expiry)
常见问题与解决方案

JWT过期策略

  • 解决方案:可以实现Token刷新机制,当Token即将过期时,客户端请求一个新的Token。这可以通过在请求头中携带旧Token来实现。

示例代码(Python)

import time

def refresh_token(old_jwt, secret):
    parts = old_jwt.split('.')
    encoded_header = parts[0]
    encoded_payload = parts[1]
    encoded_signature = parts[2]

    header = json.loads(base64.urlsafe_b64decode(encoded_header + '=='))
    payload = json.loads(base64.urlsafe_b64decode(encoded_payload + '=='))

    new_payload = {
        "sub": payload['sub'],
        "name": payload['name'],
        "iat": int(time.time())
    }

    new_jwt = create_jwt(header, new_payload, secret)
    return new_jwt

old_jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogIkpvaG4gRG9lIiwgImlhdCI6IDE1MTYyMzkwMjJ9.01J53P4V7t2qUJ8NcMn30sUfFyT1LzIaO9iB7kRQKc'
secret = b'secret_key'

new_jwt = refresh_token(old_jwt, secret)
print(new_jwt)

多客户端支持

  • 解决方案:为每个客户端生成唯一的客户端ID,并将其包含在JWT的载荷中。这样可以在服务端根据客户端ID进行更细粒度的访问控制。

示例代码(Python)

import time

def create_jwt_with_client_id(header, payload, secret, client_id):
    payload['client_id'] = client_id
    jwt = create_jwt(header, payload, secret)
    return jwt

header = {"alg": "HS256", "typ": "JWT"}
payload = {"sub": "1234567890", "name": "John Doe", "iat": int(time.time())}
secret = b'secret_key'
client_id = 'client1'

jwt = create_jwt_with_client_id(header, payload, secret, client_id)
print(jwt)

令牌刷新机制

  • 解决方案:实现Token刷新机制,当Token即将过期时,客户端请求一个新的Token。这可以通过在请求头中携带旧Token来实现。

示例代码(Python)

import time

def refresh_token(old_jwt, secret):
    parts = old_jwt.split('.')
    encoded_header = parts[0]
    encoded_payload = parts[1]
    encoded_signature = parts[2]

    header = json.loads(base64.urlsafe_b64decode(encoded_header + '=='))
    payload = json.loads(base64.urlsafe_b64decode(encoded_payload + '=='))

    new_payload = {
        "sub": payload['sub'],
        "name": payload['name'],
        "iat": int(time.time())
    }

    new_jwt = create_jwt(header, new_payload, secret)
    return new_jwt

old_jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogIkpvaG4gRG9lIiwgImlhdCI6IDE1MTYyMzkwMjJ9.01J53P4V7t2qUJ8NcMn30sUfFyT1LzIaO9iB7kRQKc'
secret = b'secret_key'

new_jwt = refresh_token(old_jwt, secret)
print(new_jwt)
點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消