使用spring security开发基于jwt验证用户身份的REST程序(Spring Boot)

假设我们已经有了登录注册接口,接下来我们在 REST API 中使用 JWT 来验证用户身份。我们将分几个步骤来实现

技术栈

Spring Boot 3.4.1
Spring Security
jjwt 0.12.6


步骤 1:添加 JWT 依赖

首先,我们需要在 build.gradle 中添加相关的 JWT 依赖。使用 jjwt 库来生成和验证 JWT。

dependencies {
        implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
        runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
        runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
}

步骤 2:创建 JWT 工具类

然后,创建一个工具类来生成和解析 JWT。

package com.example.demo.util;

import io.jsonwebtoken.*;
import java.util.Date;


public class JwtUtil {
    private final String secretKey = "4D4A614E645267554B58703273357638756F423F4428472B4B6250653368566C";  // 替换为你的密钥

    // 生成 JWT
    public String generateToken(String username) {
        return Jwts.builder()
                .subject(username)
                .issuedAt(new Date())
                .expiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    // 获取用户名
    public String extractUsername(String token) {
        return Jwts.parser()
                .setSigningKey(secretKey)
                .build()
                .parseSignedClaims(token)
                .getPayload()
                .getSubject();
    }

    // 验证 token 是否有效
    public boolean isTokenExpired(String token) {
        return Jwts.parser()
                .setSigningKey(secretKey)
                .build()
                .parseSignedClaims(token)
                .getPayload()
                .getExpiration()
                .before(new Date());
    }

    // 解析 token
    public boolean validateToken(String token, String username) {
        return (username.equals(extractUsername(token)) && !isTokenExpired(token));
    }
}


步骤 3:修改登录接口生成 JWT

在登录接口中,当用户登录成功后,生成 JWT 并返回给客户端。

package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.service.UserService;
import com.example.demo.util.JwtUtil;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class AuthController {
    private final UserService userService;
    private final JwtUtil jwtUtil = new JwtUtil();

    public AuthController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody User user) {
        if (userService.loginUser(user.getUsername(), user.getPassword())) {
            String token = jwtUtil.generateToken(user.getUsername());
            return ResponseEntity.ok(new JwtResponse(token)); // 返回 token
        }
        return ResponseEntity.badRequest().body("Login fail");
    }

}

class JwtResponse {
    private String token;

    public JwtResponse(String token) {
        this.token = token;
    }

    getter and setter...
}


步骤 4:添加 JWT 过滤器来验证每个请求

为了保护 REST API,我们需要一个过滤器来验证每个请求中的 JWT 是否有效。在 Spring Security 中,你可以通过自定义一个 OncePerRequestFilter 来实现这一点。

package com.example.demo;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import java.io.IOException;
import java.util.ArrayList;
import com.example.demo.util.JwtUtil;

@WebFilter("/*")
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
    private JwtUtil jwtUtil = new JwtUtil();

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

        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7); // 移除 "Bearer " 前缀

            // 从 token 中提取用户名并验证
            String username = jwtUtil.extractUsername(token);
            if (username != null && jwtUtil.validateToken(token, username)) {
                // 创建一个认证对象
                Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }

        filterChain.doFilter(request, response); // 继续请求处理
    }
}

步骤 5:配置 Spring Security

需要确保 Spring Security 配置正确,以便 JWT 过滤器能够生效并保护你的 API。

package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.example.demo.JwtRequestFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtRequestFilter jwtRequestFilter;

    public SecurityConfig(JwtRequestFilter jwtRequestFilter) {
        this.jwtRequestFilter = jwtRequestFilter;
    }

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

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(AbstractHttpConfigurer::disable)
//                .csrf(csrf -> csrf
//                        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
//                        .ignoringRequestMatchers("/api/login", "/api/register")
//                )
                .authorizeHttpRequests((requests) -> requests
                        .requestMatchers("/api/register", "/api/login").permitAll()
                        .anyRequest().authenticated()
                )
                // 添加 JWT 过滤器
                .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);  // 添加自定义的 JWT 过滤器

        return http.build();
    }

测试 API

  1. 使用 /login 接口登录,获取到一个 JWT。
  2. 在随后的请求中(比如访问 /test 接口),在请求头中带上 Authorization: Bearer <your-jwt-token>,来验证身份。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容