Java中JWT(JSON Web Token)的运用具体案例

 更新时间:2024年11月12日 09:36:59   作者:华农第一蒟蒻  
这篇文章主要介绍了Java中JWT(JSON Web Token)的运用具体案例,JWT(JSON Web Token)是一种开放标准,用于在网络应用环境中安全地传递信息,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境间以紧凑的方式安全地传递信息。JWT可以被用作身份验证和信息交换的手段,特别适合用于前后端分离的应用程序。

1. JWT的结构

JWT由三部分组成:

  • Header(头部): 通常包含令牌的类型(JWT)和所使用的签名算法(如HMAC SHA256或RSA)。

  • Payload(负载): 包含声明(claims),即要传递的数据。其中可以包含注册声明(如 iss、exp、sub 等)和自定义声明。

  • Signature(签名): 用于验证消息的完整性和发送者的身份。通过将编码后的header和payload与一个密钥结合,利用指定的算法生成。

JWT的格式如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

2. JWT的优点

  • 无状态: 不需要在服务器存储会话信息,减轻了服务器的负担。
  • 跨域: 可以在不同的域之间进行身份验证。
  • 自包含: 含有用户身份信息,避免频繁查询数据库。

3. JWT的流转过程

  • 用户通过登录接口发送用户名和密码。
  • 服务器验证用户信息,如果成功,则生成JWT并返回给用户。
  • 用户在后续请求中带上JWT,通常放在HTTP请求的Authorization头中。
  • 服务器解析JWT,验证其有效性,允许或拒绝请求。

4.具体案例

好的,让我们更详细地探讨如何在Java Spring Boot应用程序中实现JWT身份验证,包括每一步的代码和说明。

一、项目结构

Spring Boot项目结构如下:

src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           └── jwt
│   │               ├── JwtApplication.java
│   │               ├── config
│   │               │   └── SecurityConfig.java
│   │               ├── controller
│   │               │   └── AuthController.java
│   │               ├── model
│   │               │   └── User.java
│   │               ├── service
│   │               │   └── JwtUtil.java
│   │               └── filter
│   │                   └── JwtRequestFilter.java
│   └── resources
│       └── application.properties
└── test

二、依赖配置

在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</artifactId>
        <version>0.9.1</version>
    </dependency>
</dependencies>

三、用户模型

创建一个简单的用户模型:

package com.example.jwt.model;

public class User {
    private String username;
    private String password;

    // Constructors, getters, and setters
    //也可以使用Lombok
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}

四、JWT工具类

创建JWT工具类,负责生成和解析JWT:

package com.example.jwt.service;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
*Jwt工具类
*/
@Component//加入Spring容器
public class JwtUtil {

    private String secret = "your_secret_key"; // 强密码
    private long expiration = 60 * 60 * 1000; // 1小时

	//这个方法用于生成 JWT。它接受用户名作为参数。
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, username);
    }
	//私有方法,用于实际生成 JWT
    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()//开始构建 JWT 的构建器
                .setClaims(claims)//设置 JWT 中的声明
                .setSubject(subject)//设置主题(通常是用户名)
                .setIssuedAt(new Date(System.currentTimeMillis()))//设置 JWT 的签发时间
                .setExpiration(new Date(System.currentTimeMillis() + expiration))//设置 JWT 的过期时间
                .signWith(SignatureAlgorithm.HS256, secret)//使用指定的算法(HS256)和密钥对 JWT 进行签名
                .compact();//生成最终的 JWT 字符串
    }
	//用于验证给定的 JWT 是否有效。
    public Boolean validateToken(String token, String username) {
        final String extractedUsername = extractUsername(token);//提取 JWT 中的用户名
        return (extractedUsername.equals(username) && !isTokenExpired(token));//检查提取的用户名与提供的用户名是否匹配,并且检查 JWT 是否未过期
    }
	//从 JWT 中提取用户名
    public String extractUsername(String token) {
        return extractAllClaims(token).getSubject();
    }
	//解析 JWT 并返回所有声明
    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }//使用密钥解析 JWT 获取其主体部分(声明)
    
	//检查 JWT 是否已过期
    private Boolean isTokenExpired(String token) {
        return extractAllClaims(token).getExpiration().before(new Date());
    }
}

五、JWT请求过滤器

创建JWT请求过滤器,用于拦截请求并验证JWT:

package com.example.jwt.filter;

import com.example.jwt.service.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
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;

@Component // 将该类标记为 Spring 组件,以便自动扫描和管理
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired // 自动注入 JwtUtil 实例
    private JwtUtil jwtUtil;

    @Autowired // 自动注入 UserDetailsService 实例
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        // 从请求中获取 Authorization 头部
        final String authorizationHeader = request.getHeader("Authorization");

        String username = null; // 初始化用户名
        String jwt = null; // 初始化 JWT 令牌

        // 检查 Authorization 头部是否存在且以 "Bearer " 开头
        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            // 提取 JWT 令牌(去掉 "Bearer " 前缀)
            jwt = authorizationHeader.substring(7);
            // 从 JWT 中提取用户名
            username = jwtUtil.extractUsername(jwt);
        }

        // 如果用户名不为空且当前 SecurityContext 没有身份验证信息
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            // 根据用户名加载用户详细信息
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            
            // 验证 JWT 是否有效
            if (jwtUtil.validateToken(jwt, userDetails.getUsername())) {
                // 创建身份验证令牌,并设置用户的权限
                UsernamePasswordAuthenticationToken authenticationToken =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                
                // 设置请求的详细信息
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                
                // 将身份验证信息存入 SecurityContext
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
        
        // 继续过滤器链
        chain.doFilter(request, response);
    }
}

六、安全配置

配置Spring Security,以保护API并使用JWT:

package com.example.jwt.config;

import com.example.jwt.filter.JwtRequestFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/auth/login").permitAll() // 公开登录接口
            .anyRequest().authenticated() // 其他接口需要认证
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 无状态会话

        // 添加JWT过滤器
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user").password(passwordEncoder().encode("password")).roles("USER"); // 示例用户
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

七、身份验证控制器

创建一个控制器来处理登录请求并返回JWT:

package com.example.jwt.controller;

import com.example.jwt.model.User;
import com.example.jwt.service.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/auth")
public class AuthController {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @PostMapping("/login")
    public String login(@RequestBody User user) {
        try {
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword())
            );
        } catch (Exception e) {
            throw new RuntimeException("Invalid credentials");
        }

        final UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
        return jwtUtil.generateToken(userDetails.getUsername());
    }
}

八、测试JWT

  • 启动Spring Boot应用程序。
  • 使用Postman或其他工具测试登录接口。

请求示例:

POST /auth/login
Content-Type: application/json

{
    "username": "user",
    "password": "password"
}

响应示例:

{
    "token": "eyJhbGciOiJIUzI1NiIsInR5c..."
}
  • 使用返回的token访问受保护的资源:
GET /protected/resource
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5c...

总结 

到此这篇关于Java中JWT(JSON Web Token)运用的文章就介绍到这了,更多相关Java中JWT的运用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java面试题冲刺第二十一天--JVM

    Java面试题冲刺第二十一天--JVM

    这篇文章主要为大家分享了最有价值的三道关于JVM的面试题,涵盖内容全面,包括数据结构和算法相关的题目、经典面试编程题等,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Java中定时任务的6种实现方式

    Java中定时任务的6种实现方式

    这篇文章主要给大家分享的是Java中定时任务的6种实现方式,几乎在所有的项目中,定时任务的使用都是不可或缺的,如果使用不当甚至会造成资损,下面文章我们就来看看Java中定时任务的具体使用方式吧
    2021-10-10
  • 详解SpringIOC BeanDeifition

    详解SpringIOC BeanDeifition

    这篇文章主要介绍了SpringIOC BeanDeifition的相关资料,帮助大家更好的理解和学习springioc,感兴趣的朋友可以了解下
    2020-12-12
  • java多线程编程之使用thread类创建线程

    java多线程编程之使用thread类创建线程

    在Java中创建线程有两种方法:使用Thread类和使用Runnable接口。在使用Runnable接口时需要建立一个Thread实例
    2014-01-01
  • java修饰类的使用方法以及使用技巧(分享)

    java修饰类的使用方法以及使用技巧(分享)

    下面小编就为大家带来一篇java修饰类的使用方法以及使用技巧(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • SpringMVC实现注解式权限验证的实例

    SpringMVC实现注解式权限验证的实例

    本篇文章主要介绍了SpringMVC实现注解式权限验证的实例,可以使用Spring MVC中的action拦截器来实现,具有一定的参考价值,有兴趣的可以了解下。
    2017-02-02
  • 利用微信小程序+JAVA实现微信支付的全过程

    利用微信小程序+JAVA实现微信支付的全过程

    微信支付是一种在线支付解决方案,允许用户通过微信内的支付功能进行付款,下面这篇文章主要给大家介绍了关于利用微信小程序+JAVA实现微信支付的相关资料,需要的朋友可以参考下
    2024-08-08
  • SpringBoot使用AOP,内部方法失效的解决方案

    SpringBoot使用AOP,内部方法失效的解决方案

    这篇文章主要介绍了SpringBoot使用AOP,内部方法失效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • SpringBoot+Redis执行lua脚本的5种方式总结

    SpringBoot+Redis执行lua脚本的5种方式总结

    Lua是一种快速、轻量级的脚本语言,广泛应用于各种领域,包括数据库,Redis作为一个内嵌Lua解释器的NoSQL数据库,允许通过Lua脚本在服务器端执行一些复杂的操作,本文给大家介绍了使用SpringBoot Redis执行lua脚本的五种方式,需要的朋友可以参考下
    2023-11-11
  • dubbo服务引用之创建Invoker流程详解

    dubbo服务引用之创建Invoker流程详解

    这篇文章主要为大家介绍了dubbo服务引用二之创建Invoker流程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08

最新评论