JWT的详细讲解-- AI比我们呢更了解JWT(PS: 此文由AI生成)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: JWT的详细讲解-- AI比我们呢更了解JWT(PS: 此文由AI生成)

一、JWT详解

1.1 什么是JWT

1.1.1 JWT的定义

JWT(JSON Web Token)是一种基于JSON格式的开放标准(RFC  7519),主要用于在各个系统之间安全地传输信息。它通过使用数字签名可以验证数据的完整性,从而实现了在不同系统之间的安全通信。它的标准结构为:“aaa.bbb.ccc”,其中第一部分是Header,第二部分是Payload,第三部分是Signature。

1.1.2 JWT的优势

与传统的Session认证方式相比,JWT有以下优点:

  • 无状态:JWT通过将状态信息储存在token中,降低了服务端的状态存储压力,使服务端变得无状态化。
  • 分布式系统:JWT可以在不同的应用系统中传递,可以方便地用于分布式系统中的身份验证和授权。
  • 可定制性:JWT内置了Header、Payload和Signature三个部分,开发人员可以根据自己的需求,在Payload中添加自定义信息。

1.2 JWT的组成

1.2.1 Header

Header部分通常由两部分组成:token类型和算法类型,它们使用base64Url编码后组成一个字符串。例如:

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

其中,alg表示加密算法,typ表示token类型。JWT支持多种加密算法,如HS256、HS512、RS256等。

1.2.2 Payload

Payload是JWT的第二部分,它存放着需要传输的数据信息。Payload通常也由一些标准字段组成,比如iss、exp、sub、aud等。例如:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}
  • sub:Subject,标识JWT的主题,即用户的唯一标识。
  • name:用户名。
  • iat: Issued At时间,即JWT的签发时间。它的值为时间戳,用于标识JWT的唯一性。

除了标准字段外,开发人员也可以在Payload中添加自定义的字段,以实现更多的功能。

1.2.3 Signature

Signature是JWT的第三部分,它使用Header中指定的算法对Header和Payload进行签名,从而保证JWT的完整性和真实性。Signature的值通常由Header、Payload、密钥和加密算法组成。例如:

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

其中,secret为加密密钥,用于对Header和Payload进行签名。在验证JWT时,服务端会使用同样的算法和密钥来验证Signature的正确性。

二、JWT的使用

2.1 使用JWT

使用JWT的流程如下:

  1. 用户提供账号和密码进行登录认证;
  2. 服务端验证账号和密码的正确性,如果正确则生成JWT并返回给客户端;
  3. 客户端收到JWT后,将其储存在本地;
  4. 客户端在后续请求中携带JWT;
  5. 服务端使用相同的密钥对JWT进行验证,并根据其中的Payload信息进行相应的处理。

2.2 JWT的生成与验证

使用Java可以使用第三方库进行JWT的生成和验证。常见的库有jjwt和java-jwt。以下是使用jjwt库进行JWT的生成和验证的示例代码。

2.2.1 安装jjwt库

使用Maven配置依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

或者下载jar包手动引入。

2.2.2 生成JWT

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtUtil {
    public static String createJWT(String subject, String audience, String issuer, long ttlMillis, String secret) {
        // 签发时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        // 设置签发时间和过期时间
        Date exp = new Date(nowMillis + ttlMillis);
        // 构建JWT
        String jwt = Jwts.builder()
                .setSubject(subject)
                .setAudience(audience)
                .setIssuer(issuer)
                .setIssuedAt(now)
                .setExpiration(exp)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
        return jwt;
    }
}

其中,subject为JWT的主题,可以是用户ID;audience为JWT的受众,可以是应用名称;issuer为JWT的签发者,可以是服务器名称;ttlMillis为JWT的有效期,单位为毫秒;secret为JWT的密钥,用于签名。

2.2.3 验证JWT

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
public class JwtUtil {
    public static boolean validateJWT(String jwt, String secret) {
        try {
            // 验证JWT的签名
            Claims claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(jwt).getBody();
            // 验证JWT的过期时间
            long nowMillis = System.currentTimeMillis();
            Date now = new Date(nowMillis);
            Date exp = claims.getExpiration();
            if (exp.before(now)) {
                return false;
            }
            return true;
        } catch (SignatureException e) {
            // 验证不通过
            return false;
        }
    }
}

其中,jwt为待验证的JWT字符串,secret为JWT的密钥。该方法会返回一个boolean值,表示JWT验证是否通过。

2.3 JWT的使用示例

以下是一个使用JWT进行身份验证的示例。假设有一个AdminController,该类的所有方法都需要进行身份验证。在每个请求中,客户端需要在Authorization Header中携带JWT。服务端在验证JWT通过后才会继续处理请求。

import io.jsonwebtoken.*;
@RestController
@RequestMapping("/admin")
public class AdminController {
    @Autowired
    private AdminService adminService;
    @Value("${jwt.secret}")
    private String secret;
    @PostMapping("/login")
    public ResponseEntity login(@RequestBody Admin admin) {
        // 验证账号和密码
        boolean authenticated = adminService.authenticate(admin);
        if (!authenticated) {
            return ResponseEntity.badRequest().build();
        }
        // 生成JWT
        String jwt = JwtUtil.createJWT(admin.getId(), "admin", "MyApp", 3600000L, secret);
        return ResponseEntity.ok(jwt);
    }
    @GetMapping("/users")
    public ResponseEntity getUsers(@RequestHeader("Authorization") String auth) {
        // 验证JWT
        String jwt = auth.substring(7);
        boolean validated = JwtUtil.validateJWT(jwt, secret);
        if (!validated) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
        // 处理请求
        List<User> userList = adminService.getUsers();
        return ResponseEntity.ok(userList);
    }
}

在登录时,AdminController会验证账号和密码,如果成功,则生成JWT并返回给客户端。在后续请求中,客户端需要在Authorization Header中携带JWT。服务端在验证JWT通过后才会继续处理请求。

三、JWT的实践

3.1 Spring Boot 集成 JWT

在 Spring Boot 中集成 JWT,可以使用 JJwt 库,该库已经提供了 JWT 的创建、验证以及解析功能。在使用该库之前,需要在 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.10.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.10.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.10.5</version>
</dependency>

其中,jjwt-api 是 JWT 的 API 接口,jjwt-impl 是 JWT 的实现,jjwt-jackson 是为了支持 JWT 对象的序列化。

在 Spring Boot 中使用 JWT 的步骤如下:

3.1.1. 定义 JWT 的生成和解析接口

public interface JwtTokenProvider {
    String generateToken(Authentication authentication);
    boolean validateToken(String token);
    String getUsernameFromToken(String token);
}

在接口中,定义了生成 Token、验证 Token 和从 Token 中获取用户名的方法。

3.1.2. 实现 JWT 的生成和解析接口

@Service
public class JwtTokenProviderImpl implements JwtTokenProvider {
    private static final Logger logger = LoggerFactory.getLogger(JwtTokenProviderImpl.class);
    @Value("${app.jwtSecret}")
    private String jwtSecret;
    @Value("${app.jwtExpirationInMs}")
    private int jwtExpirationInMs;
    @Override
    public String generateToken(Authentication authentication) {
        UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
        return Jwts.builder()
                .setSubject(Long.toString(userPrincipal.getId()))
                .setIssuedAt(new Date())
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }
    @Override
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
            return true;
        } catch (SignatureException ex) {
            logger.error("Invalid JWT signature");
        } catch (MalformedJwtException ex) {
            logger.error("Invalid JWT token");
        } catch (ExpiredJwtException ex) {
            logger.error("Expired JWT token");
        } catch (UnsupportedJwtException ex) {
            logger.error("Unsupported JWT token");
        } catch (IllegalArgumentException ex) {
            logger.error("JWT claims string is empty.");
        }
        return false;
    }
    @Override
    public String getUsernameFromToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(token)
                .getBody();
        return claims.getSubject();
    }
}

在实现类中,使用 Jwts 对象生成 Token,使用 setSigningKey 方法设置签名密钥,使用 parseClaimsJws 方法解析 Token,使用getBody 方法获取 Token 中存储的所有信息。

3.1.3. 在 Spring Security 中使用 JWT

在 Spring Security 中使用 JWT,需要进行以下配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtTokenProvider jwtTokenProvider;
    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter(jwtTokenProvider, userDetailsService);
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
            .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .antMatchers("/api/user/checkUsernameAvailability", "/api/user/checkEmailAvailability").permitAll()
            .anyRequest().authenticated();
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

其中,JwtAuthenticationFilter 是自定义的过滤器,用于拦截请求并验证 Token。需要注意,使用 JWT 的时候,需要设置 Spring Security 的 sessionCreationPolicy 为 STATELESS。

3.2 使用 JWT 进行认证和授权

使用 JWT 进行认证和授权的过程如下:

  1. 用户向服务器发送请求。
  2. 服务器收到请求,生成 JWT。
  3. 服务器将 JWT 发送给客户端。
  4. 客户端将 JWT 存储在本地。
  5. 客户端向服务器发送请求,使用存储的 JWT 进行认证和授权。
  6. 服务器验证 JWT,验证通过后返回请求的内容。
  7. 客户端使用返回的内容进行操作。

在使用 JWT 进行认证和授权的时候,需要注意以下几点:

  1. JWT 中需要包含用户的信息,例如用户 ID、用户名等。
  2. JWT 的签名密钥需要保密,不能泄露给外部。
  3. JWT 需要设置过期时间。
  4. JWT 不应该包含敏感信息,例如密码等。

考虑到这些问题,使用 JWT 进行认证和授权的时候需要根据实际情况进行选择,并进行相应的安全设置。

四、JWT的安全

JWT在实现认证和授权的同时,也需要考虑安全性。本章将介绍JWT的安全问题及相关解决方案。

4.1. JWT的漏洞

在使用JWT时,需要注意以下几个方面的安全问题:

  • 私钥保护问题:JWT的签发是由服务端进行的,所以服务端需要妥善保管自己的私钥。如果私钥泄露,攻击者可以伪造JWT,篡改数据,危害系统安全。
  • Token劫持问题:JWT在传输过程中可能被第三方窃取,攻击者可以利用JWT进行伪造请求,篡改数据等恶意行为。因此,JWT需要采用HTTPS协议进行传输,同时服务端也应在JWT中加入一些防篡改的信息,比如过期时间、签名等。
  • 敏感信息问题:JWT中的Payload部分是明文存储的,如果将敏感信息存入Payload中,在JWT被反解出来后会泄露敏感信息。因此,JWT中只应存放一些基本信息,敏感信息应该存放在服务端中。

4.2. JWT的安全解决方案

为解决JWT存在的安全问题,有以下几种方案:

  • 选择合适的算法:JWT中的签名算法影响着JWT的安全性,应根据系统的实际情况选择合适的算法。建议采用RSA算法或HMAC-SHA256算法,不建议使用HMAC-SHA1算法。
  • 签名时加盐:为防止黑客破解签名,应在签名时加盐,使攻击者更难猜测签名的值,进而增强JWT的安全性。
  • 使用HTTPS协议传输:在传输JWT时,应使用HTTPS协议进行传输,避免Token被第三方窃取。
  • 设置Token的过期时间:为了防止Token被长期滥用,应在Token中设置过期时间。
  • JWT中不存储敏感信息:为了保护敏感信息,JWT中只应存放一些基本信息,敏感信息应该存放在服务端中。

除了以上方案,还可以使用一些第三方的JWT安全解决方案,比如Auth0提供的JWT黑名单、JWT白名单,可以对Token进行有效性判断。

五、总结

本文介绍了JSON Web Token(JWT)的基础知识、原理、应用场景、Spring Boot集成以及安全问题及解决方案。JWT作为一种轻量级的认证和授权机制,具有简单、可扩展性好、支持跨域等优点,在Web开发中得到了广泛的应用。

5.1. 优点

  • 轻量级:JWT是一种轻量级的认证和授权机制,Token的体积很小,传输速度快。
  • 自包含:JWT是一种自包含的Token,里面包含了用户的身份信息,无需再向数据库查询,减少了服务器的压力。
  • 可扩展性好:JWT中的Payload可以存放任何数据,具有很好的扩展性。
  • 支持跨域:JWT可以跨域传输,适用于多种场景。

5.2. 缺点

  • 安全性:JWT存在私钥保护、Token劫持、敏感信息泄露等安全问题。
  • 无法撤回:一旦JWT被签发,就无法撤回,除非Token过期。
  • 需要存储Payload:JWT中的Payload部分是明文存储的,如果存储敏感信息,会存在信息泄露的风险。

5.3. 应用场景

  • 单点登录(SSO):JWT可以在多个应用之间共享用户信息,实现单点登录。
  • 前后端分离:前后端分离的应用中,JWT作为一种轻量级的认证和授权机制,可以方便的实现用户登录。
  • 无状态认证:JWT的自包含特性可以实现无状态认证,减少服务器压力。

5.4. 未来

随着互联网的发展,移动端应用和Web应用的用户认证和授权需求越来越多,JWT作为一种轻量级的认证和授权机制,具有应用价值。未来,在不断的技术迭代中,JWT也会愈加成熟、完善,可能出现更多的安全性强、使用更加方便的JWT实现方案。

参考资料

相关文章
|
3天前
|
人工智能 自然语言处理 网络协议
ps beta ai显示高峰需求进不去怎么办? psai高峰期需求用不了解决办法
PSBetaAI2023加入了AI的功能,在使用过程中,有时会遇到一个令人烦恼的问题,那就是PhotoshopBetaAI提示我们正在面临高峰需求,请稍候再试,针对这个问题,本文为大家整理了几个可行的解决方法,可以根据自己的实际情况来尝试解决
30 12
|
6月前
|
人工智能 前端开发 iOS开发
ui设计_入门ai、ps
ui设计_入门ai、ps
64 0
|
人工智能 自然语言处理 开发者
PS上的开源Stable Diffusion插件来了:一键AI脑补,即装即用
PS上的开源Stable Diffusion插件来了:一键AI脑补,即装即用
1011 1
|
机器学习/深度学习 人工智能 搜索推荐
|
6月前
|
Linux
百度搜索:蓝易云【Linux系统ps命令:查看正在运行的进程】
通过这些简洁的ps命令用法,你可以方便地查看Linux系统中正在运行的进程信息。
86 1
|
6月前
|
安全 Linux 应用服务中间件
linux(三十一)系统信息命令ps查看系统进程
linux(三十一)系统信息命令ps查看系统进程
216 1
|
6月前
|
存储 监控 Linux
【Shell 命令集合 系统管理 】⭐⭐⭐Linux 查看当前正在运行的进程信息 ps命令 使用指南
【Shell 命令集合 系统管理 】⭐⭐⭐Linux 查看当前正在运行的进程信息 ps命令 使用指南
112 0
|
5月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
120 13