Java中JWT(JSON Web Token)的运用

华农第一蒟蒻 2024-10-07 10:03:01 阅读 73

目录

1. JWT的结构2. JWT的优点3. JWT的流转过程4.具体案例一、项目结构二、依赖配置三、用户模型四、JWT工具类五、JWT请求过滤器六、安全配置七、身份验证控制器八、测试JWT

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

1. JWT的结构

JWT由三部分组成:

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

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

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

JWT的格式如下:

<code>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...



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。