当前位置: 首页 > news >正文

十一、Spring Boot:使用JWT实现用户认证深度解析

Spring Boot JWT(JSON Web Token):无状态认证

在现代 Web 开发中,无状态认证是一种重要的安全机制,它允许服务器在不存储会话信息的情况下验证用户身份。JSON Web Token(JWT)是一种常用的无状态认证技术,它通过一个紧凑的 URL 安全令牌来传递用户身份信息。Spring Boot 与 JWT 的结合可以为应用提供强大的安全保护。本文将详细介绍如何在 Spring Boot 中集成 JWT 实现无状态认证。

一、JWT 概述

1.1 什么是 JWT

JWT 是一种基于 JSON 的开放标准(RFC 7519),用于在各方之间传递声明信息。它由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。JWT 的主要特点是无状态、自包含和安全,非常适合用于分布式系统和微服务架构中的身份验证和信息交换。

1.2 JWT 的工作原理

当用户使用用户名和密码登录系统时,认证服务器会验证用户的身份。如果验证成功,服务器会生成一个 JWT,并将其返回给客户端。客户端在后续的请求中,将 JWT 放在请求头的 Authorization 字段中,格式为 Bearer <token>。服务器接收到请求后,会验证 JWT 的合法性,如果验证通过,则允许用户访问受保护的资源。

二、Spring Boot 集成 JWT

2.1 添加依赖

在 Spring Boot 项目中,需要在 pom.xml 文件中添加 JWT 和 Spring Security 的依赖。

<!-- Spring Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

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

2.2 配置 JWT 工具类

创建一个工具类 JwtUtil,用于生成和验证 JWT。

import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtUtil {
    @Value("${jwt.secret}")
    private String secret;

    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(username)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 小时有效期
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        return claimsResolver.apply(claims);
    }

    public Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    public Boolean validateToken(String token, String username) {
        final String extractedUsername = extractUsername(token);
        return (extractedUsername.equals(username) && !isTokenExpired(token));
    }
}

2.3 配置 Spring Security

创建一个 SecurityConfig 类,配置 Spring Security 以支持 JWT 认证。

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.core.userdetails.UserDetailsService;
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;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/authenticate").permitAll()
            .anyRequest().authenticated()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }

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

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

2.4 创建 JWT 请求过滤器

创建一个 JwtRequestFilter 类,用于在每个请求中解析 JWT,并将其设置到 Spring Security 上下文中。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
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
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserDetailsService userDetailsService;

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

        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = jwtUtil.extractUsername(jwt);
        }

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

            if (jwtUtil.validateToken(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }

        chain.doFilter(request, response);
    }
}

2.5 创建认证和授权接口

创建一个 AuthenticationController 类,用于处理用户登录和生成 JWT。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class AuthenticationController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @PostMapping("/authenticate")
    public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {
        try {
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
            );
        } catch (BadCredentialsException e) {
            throw new Exception("Incorrect username or password", e);
        }

        final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
        final String jwt = jwtUtil.generateToken(userDetails.getUsername());

        return ResponseEntity.ok(new AuthenticationResponse(jwt));
    }
}

2.6 创建用户详情服务

创建一个 UserDetailsService 实现类,用于加载用户信息。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));

        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());
    }
}

2.7 配置 application.properties

application.properties 文件中配置 JWT 密钥。

jwt.secret=yourSecretKey

2.8 测试

启动应用后,可以通过发送 POST 请求到 /api/authenticate 接口进行登录,获取 JWT。然后在请求头中添加 Authorization: Bearer <token>,即可访问受保护的接口。

三、总结

通过本文的介绍,我们详细学习了如何在 Spring Boot 中集成 JWT 实现无状态认证。我们添加了必要的依赖,配置了 JWT 工具类、Spring Security、JWT 请求过滤器、认证和授权接口以及用户详情服务。希望这些内容能够帮助你在实际开发中更好地应用 JWT,提升应用的安全性。

相关文章:

  • 飞鹤奶粉三度问鼎 TPM 大奖
  • IO学习day4
  • qt 播放pcm音频
  • 06实现相册小项目
  • 个人学习编程(3-06) 树形数据结构
  • Go语言里面的堆跟栈 + new 和 make + 内存逃逸 + 闭包
  • URL中的特殊字符与web安全
  • uniapp封装路由管理(兼容Vue2和Vue3)
  • module ‘matplotlib‘ has no attribute ‘colormaps‘
  • phpstorm 无法重建文件
  • 统信UOS上AI辅助绘图:用DeepSeek+draw.io生成流程图
  • NET400协议网关(老款GRM300):跨品牌PLC与多协议数据整合解决方案,包含NET421,NET422,NET431等
  • 单细胞分析(22)——高效使用 Cell Ranger:安装、参数解析及 Linux 后台运行指南
  • Mysql中的常用函数
  • 系统架构设计师—数据库基础篇—数据库规范化
  • RxJava 用法封装举例
  • 初中文凭怎么成人大专-一种简单省心的方式
  • Gauss数据库omm用户无法连接处理
  • 写作思维魔方
  • 下载PyCharm 2024.3.4 (Community Edition)来开发测试python
  • 网站分类 维护/广州网站优化工具
  • 公司取名字大全免费查询2022/seo关键词排名优化如何
  • 网站建设规划/网站优化的关键词
  • 阿里主机 wordpress/seo的基本内容
  • 乐清网站设计公司哪家好/seo三人行网站
  • 介绍几个有趣的网站/武汉大学人民医院院长