第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

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

JWT單點登錄原理資料詳解

標(biāo)簽:
云計算 安全 API
概述

本文详细介绍了JWT单点登录的原理和实现步骤,涵盖了JWT的工作机制、JWT与单点登录的关系以及JWT单点登录的安全措施。文中还提供了使用Node.js和Spring Boot实现JWT单点登录的实际示例代码。JWT单点登录原理资料将帮助读者全面了解并实现这一安全认证机制。

JWT简介
什么是JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传递信息。JWT的设计目的是紧凑且易于使用,适合通过HTTP请求的头部进行传输。JWT通常用于身份验证,但也可以用于信息交换。JWT由三部分组成:头部(Header)、负载(Payload)和签名(Signature)。这三部分分别负责不同的功能。

JWT的工作原理

JWT的工作原理如下:

  1. 生成JWT令牌: 客户端请求服务器,请求携带用户名和密码,服务器验证用户信息后,生成一个JWT令牌。
  2. 发送JWT令牌: 服务器将生成的JWT令牌通过HTTP响应返回给客户端。
  3. 存储JWT令牌: 客户端接收到JWT令牌后,通常会将其存储在本地(如浏览器的local storage或session storage中)。
  4. 发送JWT令牌: 当用户尝试访问受保护的资源时,客户端会将JWT令牌通过HTTP请求的头部或作为URL参数发送给服务器。
  5. 验证JWT令牌: 服务器接收到JWT令牌后,进行验证,验证通过后,允许访问受保护的资源。
生成JWT令牌的具体代码示例

以下为生成JWT令牌的具体代码示例:

const jwt = require('jsonwebtoken');

const token = jwt.sign({
    id: 1,
    username: 'user123'
}, 'secret-key', { expiresIn: '1h' });

console.log(token);
JWT的组成部分
  • 头部(Header): 由两部分组成,即令牌的类型(指定为JWT)以及所使用的签名算法(如HMAC SHA256或RSA)。
    {
    "typ": "JWT",
    "alg": "HS256"
    }
  • 负载(Payload): 由声明(Claim)组成,包含各种声明。声明分为三类:
    • 标准声明(Registered Claim): 这些声明有预定义的名字及用途。例如iss(发行者)、exp(过期时间)、iat(发行时间)等。
    • 公开声明(Public Claim): 除了标准声明外,可以在JWT中包含任何其他声明(如用户ID、用户名、手机号等信息)。
    • 私人声明(Private Claim): 私人声明是自定义声明,一般用于扩展,例如添加一些私有的声明,它们通常不使用。
      {
      "sub": "1234567890",
      "name": "John Doe",
      "iat": 1516239022
      }
  • 签名(Signature): 签名部分是指加密签名,用于验证消息的真实性和完整性。它由头部、负载和一个密钥组合在一起,通过加密算法生成。如HMACSHA256RS256等。
    HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret
    )
单点登录(Single Sign On)概述
什么是单点登录

单点登录(Single Sign On, SSO)是一种身份验证方法,允许用户使用一组凭据(如用户名和密码)登录一个系统,然后自动访问所有其他已关联的受保护系统,而无需用户进行额外的身份验证。这样可以避免用户重复输入用户名和密码的过程,从而提升用户体验。

单点登录的好处
  1. 简化用户操作: 用户只需要登录一次,即可访问多个应用程序或系统。
  2. 提升安全性: 通过集中化管理用户身份验证,可以简化安全措施的实施和管理。
  3. 提高效率: IT团队可以减少维护多套身份验证系统的负担,提高工作效率。
  4. 减少错误: 避免用户因频繁输入密码而引起的操作失误。
单点登录的应用场景
  • 企业内部应用: 企业内部的各个应用系统可以通过SSO实现统一的身份验证,例如ERP系统、CRM系统、OA系统等。
  • 多平台登录: 网站或应用可以通过SSO实现跨平台登录,例如Web端、移动端、桌面端等。
  • 多租户系统: 多租户系统中,租户之间可以共享身份验证,简化租户管理。
  • 云服务平台: 多个云服务提供商可以提供统一的登录入口,用户通过一个账号可以访问多个服务。
JWT与单点登录的关系
为什么使用JWT实现单点登录

JWT非常适合用于实现单点登录,因为它具有以下优点:

  1. 无状态: 服务器端不需要存储任何状态信息,只需要验证令牌即可。
  2. 紧凑且易于传输: JWT令牌比较小,易于在网络中传输。
  3. 广泛支持: 大多数编程语言都支持JWT,便于跨平台实现。
  4. 安全性: 通过加密确保令牌的安全性。
JWT如何支持单点登录

JWT支持单点登录主要通过以下步骤实现:

  1. 统一身份验证: 当用户首次登录时,服务器验证用户身份并生成一个JWT令牌。
  2. 令牌缓存: 将生成的JWT令牌缓存在客户端(如浏览器的session或localStorage)。
  3. 令牌传递: 当用户访问其他服务时,客户端在请求中携带JWT令牌。
  4. 令牌验证: 目标服务接收请求后,通过验证JWT令牌来确认用户身份。
JWT单点登录实现步骤
用户身份验证

用户身份验证是JWT单点登录的第一步,需要验证用户提供的凭证(通常是用户名和密码)是否有效。

用户身份验证的具体代码示例

// 前端收集用户凭证
fetch('/login', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        username: 'user123',
        password: 'password123'
    })
})
.then(response => response.json())
.then(data => {
    if (data.success) {
        console.log('Login successful');
    } else {
        console.log('Login failed');
    }
});

// 后端验证用户凭证
const express = require('express');
const bcrypt = require('bcryptjs');
const app = express();
app.use(express.json());

const users = [
    {
        id: 1,
        username: 'user123',
        password: '$2a$10$K93j62oF7zKu35Z6L67hce'
    }
];

app.post('/login', (req, res) => {
    const { username, password } = req.body;
    const user = users.find(u => u.username === username);

    if (user && bcrypt.compareSync(password, user.password)) {
        const token = jwt.sign({ id: user.id }, 'secret-key', { expiresIn: '1h' });
        res.json({ success: true, token });
    } else {
        res.json({ success: false });
    }
});

const jwt = require('jsonwebtoken');
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
生成JWT令牌

当用户成功通过身份验证后,服务器将生成一个JWT令牌,这个令牌包含用户的某些信息(如用户ID、用户名等)。

const jwt = require('jsonwebtoken');

const token = jwt.sign({
    id: 1,
    username: 'user123'
}, 'secret-key', { expiresIn: '1h' });

console.log(token);
访问资源时验证JWT令牌

当用户访问受保护的资源时,服务器需要验证客户端提供的JWT令牌是否有效。

访问资源时验证JWT令牌的具体代码示例

// 前端发送请求
fetch('/protected-endpoint', {
    method: 'GET',
    headers: {
        'Authorization': 'Bearer ' + token
    }
})
.then(response => response.json())
.then(data => {
    console.log(data);
});

// 后端验证令牌
app.get('/protected-endpoint', (req, res) => {
    const token = req.headers.authorization.split(' ')[1];
    if (!token) {
        return res.status(401).json({ message: 'No token provided' });
    }

    jwt.verify(token, 'secret-key', (err, decoded) => {
        if (err) {
            return res.status(401).json({ message: 'Failed to authenticate token' });
        }

        res.json({ message: 'Access granted', user: decoded });
    });
});
JWT单点登录的安全措施
如何保护JWT的安全

保护JWT的安全性是实现单点登录的重要部分。以下是一些常见的安全措施:

  • 密钥管理: 使用强大的密钥管理策略来保护用于生成JWT的密钥。
  • 令牌签名: 使用加密算法(如HMAC、RSA)对JWT进行签名,以确保令牌的完整性。
  • 令牌有效期: 设置合理的令牌有效时间,过期的令牌不再具有有效性。
  • 令牌刷新: 提供令牌刷新机制,以便在令牌即将过期时自动获取新的令牌。
  • 令牌信息: 尽量减少在负载中包含敏感信息,只传递必要的信息。
常见的安全问题及解决方案
  • 恶意用户攻击: 恶意用户可能尝试猜测或盗取他人的JWT令牌。
    1. 解决方案: 对JWT令牌进行签名,并设置令牌的有效时间,过期后的令牌不再有效。
  • 中间人攻击: 中间人攻击者可能通过中间层截取和篡改JWT令牌。
    1. 解决方案: 使用HTTPS协议保护数据传输的安全性,避免中间人攻击。
  • 令牌丢失: 用户可能丢失JWT令牌,例如在关闭浏览器窗口时。
    1. 解决方案: 提供令牌刷新功能,用户在令牌即将过期时可以自动获取新的令牌。
  • 令牌篡改: 用户可能试图篡改JWT负载中的信息以获取未经授权的访问。
    1. 解决方案: 在负载中包含签名,使用加密算法确保令牌的完整性。
实践示例
使用Node.js实现JWT单点登录

依赖安装

npm install express jsonwebtoken bcryptjs express-session

前端代码

<!DOCTYPE html>
<html>
<head>
    <title>JWT SSO</title>
    <script>
        async function login() {
            const response = await fetch('/login', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    username: 'user123',
                    password: 'password123'
                })
            });

            const data = await response.json();
            if (data.success) {
                localStorage.setItem('token', data.token);
                alert('Login successful');
            } else {
                alert('Login failed');
            }
        }

        function logout() {
            localStorage.removeItem('token');
            alert('Logout successful');
        }
    </script>
</head>
<body>
    <button onclick="login()">Login</button>
    <button onclick="logout()">Logout</button>
</body>
</html>

后端代码

const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const session = require('express-session');
const bodyParser = require('body-parser');

const app = express();
const PORT = process.env.PORT || 3000;

app.use(bodyParser.json());
app.use(session({
    secret: 'secret-key', // 用于签名session对象的字符串
    resave: false,
    saveUninitialized: true
}));

const users = [
    {
        id: 1,
        username: 'user123',
        password: '$2a$10$K93j62oF7zKu35Z6L67hce'
    }
];

app.post('/login', (req, res) => {
    const { username, password } = req.body;
    const user = users.find(u => u.username === username);

    if (user && bcrypt.compareSync(password, user.password)) {
        const token = jwt.sign({ id: user.id }, 'secret-key', { expiresIn: '1h' });
        res.json({ success: true, token });
    } else {
        res.json({ success: false });
    }
});

app.get('/protected-endpoint', (req, res) => {
    const token = req.headers.authorization.split(' ')[1];
    if (!token) {
        return res.status(401).json({ message: 'No token provided' });
    }

    jwt.verify(token, 'secret-key', (err, decoded) => {
        if (err) {
            return res.status(401).json({ message: 'Failed to authenticate token' });
        }

        res.json({ message: 'Access granted', user: decoded });
    });
});

app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
使用Spring Boot实现JWT单点登录

依赖管理

pom.xml中添加以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.2</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.2</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.2</version>
    </dependency>
</dependencies>

前端代码

<!DOCTYPE html>
<html>
<head>
    <title>JWT SSO</title>
    <script>
        async function login() {
            const response = await fetch('/login', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    username: 'user123',
                    password: 'password123'
                })
            });

            const data = await response.json();
            if (data.success) {
                localStorage.setItem('token', data.token);
                alert('Login successful');
            } else {
                alert('Login failed');
            }
        }

        function logout() {
            localStorage.removeItem('token');
            alert('Logout successful');
        }
    </script>
</head>
<body>
    <button onclick="login()">Login</button>
    <button onclick="logout()">Logout</button>
</body>
</html>

后端代码

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.MacSigner;
import com.nimbusds.jose.crypto.MacVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;

@Service
public class JwtUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if ("user123".equals(username)) {
            return new User("user123",
                    new BCryptPasswordEncoder().encode("password123"),
                    new ArrayList<>());
        } else {
            throw new UsernameNotFoundException("User not found");
        }
    }
}
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}
@Service
public class JwtTokenUtil {
    private final PasswordEncoder encoder;

    public JwtTokenUtil(PasswordEncoder encoder) {
        this.encoder = encoder;
    }

    public String generateToken(UserDetails userDetails) {
        try {
            JWSHeader header = new JWSHeader(JWSAlgorithm.HS256);
            JWTClaimsSet claims = new JWTClaimsSet.Builder()
                .subject(userDetails.getUsername())
                .claim("authorities", userDetails.getAuthorities())
                .issueTime(new Date())
                .expirationTime(new Date(System.currentTimeMillis() + 3600000L))
                .build();
            SignedJWT signedJWT = new SignedJWT(header, claims);
            signedJWT.sign(new MacSigner(encoder.encode("secret-key")));
            return signedJWT.serialize();
        } catch (JOSEException e) {
            throw new RuntimeException(e);
        }
    }

    public boolean validateToken(String token, UserDetails userDetails) {
        try {
            SignedJWT signedJWT = SignedJWT.parse(token);
            MacVerifier verifier = new MacVerifier(encoder.encode("secret-key"));
            return signedJWT.verify(verifier) && signedJWT.getJWTClaimsSet().getSubject().equals(userDetails.getUsername());
        } catch (JOSEException e) {
            throw new RuntimeException(e);
        }
    }

    public boolean validateToken(String token) {
        try {
            SignedJWT signedJWT = SignedJWT.parse(token);
            MacVerifier verifier = new MacVerifier(encoder.encode("secret-key"));
            return signedJWT.verify(verifier);
        } catch (JOSEException e) {
            throw new RuntimeException(e);
        }
    }
}
import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.jwt.JWTClaimsSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;

@Component
public class JwtRequestFilter extends OncePerRequestFilter {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService jwtInMemoryUserDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        final String requestTokenHeader = request.getHeader("Authorization");

        String username = null;
        String jwtToken = null;
        // JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                JWTClaimsSet claims = jwtTokenUtil.parseClaims(jwtToken);
                username = claims.getSubject();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = jwtInMemoryUserDetailsService.loadUserByUsername(username);

            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null,
                        userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(request, response);
    }
}

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.bind.annotation.*;

import java.util.Collections;
import java.util.Map;

@RestController
public class JwtAuthenticationController {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService jwtInMemoryUserDetailsService;

    @PostMapping("/login")
    public Map<String, String> createAuthenticationToken(@RequestBody JwtRequest jwtRequest) throws Exception {
        authenticate(jwtRequest.getUsername(), jwtRequest.getPassword());

        final UserDetails userDetails = jwtInMemoryUserDetailsService.loadUserByUsername(jwtRequest.getUsername());

        final String token = jwtTokenUtil.generateToken(userDetails);

        return Collections.singletonMap("token", token);
    }

    private void authenticate(String username, String password) throws Exception {
        Objects.requireNonNull(username);
        Objects.requireNonNull(password);
        UserDetails userDetails = jwtInMemoryUserDetailsService.loadUserByUsername(username);

        if (!encoder.matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException("Invalid username or password");
        }

        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                userDetails, null,
                Collections.<GrantedAuthority>emptyList()
        );
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    @GetMapping("/protected-endpoint")
    public Map<String, String> getProtectedResource(HttpServletRequest request) {
        String token = request.getHeader("Authorization");

        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);

            try {
                JWTClaimsSet claims = jwtTokenUtil.parseClaims(token);
                String username = claims.getSubject();

                if (jwtTokenUtil.validateToken(token, jwtInMemoryUserDetailsService.loadUserByUsername(username))) {
                    return Collections.singletonMap("response", "Access granted");
                } else {
                    return Collections.singletonMap("response", "Invalid token");
                }
            } catch (Exception e) {
                return Collections.singletonMap("response", "Invalid token");
            }
        } else {
            return Collections.singletonMap("response", "No token");
        }
    }
}
``

通过以上示例,可以理解如何在不同的技术栈中实现JWT单点登录,包括前端和后端的实现细节。
點擊查看更多內(nèi)容
TA 點贊

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

評論

作者其他優(yōu)質(zhì)文章

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

100積分直接送

付費專欄免費學(xué)

大額優(yōu)惠券免費領(lǐng)

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

購課補貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學(xué)習(xí)伙伴

公眾號

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號

舉報

0/150
提交
取消