Java案例如何实现JWT令牌认证?

wen java案例 3

本文目录导读:

Java案例如何实现JWT令牌认证?

  1. 添加依赖
  2. JWT工具类
  3. 用户认证Controller
  4. JWT认证过滤器
  5. Spring Security配置
  6. 受保护的API示例
  7. 测试示例
  8. 使用Interceptor方式(不依赖Spring Security)
  9. 前端调用示例
  10. 最佳实践建议

我来详细说明如何在Java中实现JWT令牌认证,包含完整的示例代码。

添加依赖

Maven依赖

<!-- JWT -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

JWT工具类

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
    // 密钥(生产环境建议从配置文件中读取)
    private static final String SECRET = "YourSuperSecretKeyForJWTTokenGeneration2024!@#$%^&*()";
    // 过期时间(24小时)
    private static final long EXPIRATION_TIME = 24 * 60 * 60 * 1000;
    private static SecretKey getSigningKey() {
        return Keys.hmacShaKeyFor(SECRET.getBytes());
    }
    /**
     * 生成JWT令牌
     */
    public static String generateToken(String username, Map<String, Object> claims) {
        if (claims == null) {
            claims = new HashMap<>();
        }
        return Jwts.builder()
                .setClaims(claims)  // 自定义声明
                .setSubject(username)  // 主题(通常是用户名)
                .setIssuedAt(new Date())  // 签发时间
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))  // 过期时间
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)  // 签名算法
                .compact();
    }
    /**
     * 解析JWT令牌
     */
    public static Claims parseToken(String token) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(getSigningKey())
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        } catch (JwtException | IllegalArgumentException e) {
            throw new RuntimeException("JWT令牌无效或已过期");
        }
    }
    /**
     * 验证令牌是否有效
     */
    public static boolean validateToken(String token) {
        try {
            Jwts.parserBuilder()
                    .setSigningKey(getSigningKey())
                    .build()
                    .parseClaimsJws(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false;
        }
    }
    /**
     * 从令牌中获取用户名
     */
    public static String getUsernameFromToken(String token) {
        Claims claims = parseToken(token);
        return claims.getSubject();
    }
    /**
     * 检查令牌是否过期
     */
    public static boolean isTokenExpired(String token) {
        try {
            Claims claims = parseToken(token);
            return claims.getExpiration().before(new Date());
        } catch (Exception e) {
            return true;
        }
    }
}

用户认证Controller

import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    // 模拟用户数据库
    private static final Map<String, String> USERS = new HashMap<>();
    static {
        USERS.put("admin", "admin123");
        USERS.put("user", "user123");
    }
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
        String username = loginRequest.getUsername();
        String password = loginRequest.getPassword();
        // 验证用户名密码
        if (USERS.containsKey(username) && USERS.get(username).equals(password)) {
            // 创建自定义声明
            Map<String, Object> claims = new HashMap<>();
            claims.put("role", username.equals("admin") ? "ADMIN" : "USER");
            // 生成JWT令牌
            String token = JwtUtil.generateToken(username, claims);
            // 返回令牌信息
            Map<String, Object> response = new HashMap<>();
            response.put("token", token);
            response.put("username", username);
            response.put("expiresIn", 24 * 60 * 60); // 秒
            return ResponseEntity.ok(response);
        } else {
            return ResponseEntity.status(401).body("用户名或密码错误");
        }
    }
}
// 登录请求对象
class LoginRequest {
    private String username;
    private String password;
    // getter和setter方法
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}

JWT认证过滤器

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
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;
import java.util.ArrayList;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) 
            throws ServletException, IOException {
        String token = extractTokenFromRequest(request);
        if (token != null && JwtUtil.validateToken(token)) {
            String username = JwtUtil.getUsernameFromToken(token);
            // 创建认证对象
            UsernamePasswordAuthenticationToken authentication = 
                new UsernamePasswordAuthenticationToken(
                    username, null, new ArrayList<>());
            // 设置认证信息到安全上下文
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        filterChain.doFilter(request, response);
    }
    /**
     * 从请求中提取JWT令牌
     */
    private String extractTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

Spring Security配置

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.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()  // 禁用CSRF保护
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)  // 无状态会话
            .and()
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()  // 认证接口不需要认证
                .antMatchers("/api/admin/**").hasRole("ADMIN")  // 管理员接口
                .antMatchers("/api/**").authenticated()  // 其他API需要认证
            .and()
            .addFilterBefore(new JwtAuthenticationFilter(), 
                             UsernamePasswordAuthenticationFilter.class);  // 添加JWT过滤器
        return http.build();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

受保护的API示例

import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class UserController {
    @GetMapping("/profile")
    public ResponseEntity<?> getProfile(@RequestHeader("Authorization") String token) {
        // 从令牌中获取用户信息
        String jwtToken = token.replace("Bearer ", "");
        String username = JwtUtil.getUsernameFromToken(jwtToken);
        Map<String, Object> profile = new HashMap<>();
        profile.put("username", username);
        profile.put("email", username + "@example.com");
        profile.put("createTime", "2024-01-01");
        return ResponseEntity.ok(profile);
    }
    @GetMapping("/admin/users")
    public ResponseEntity<?> getAllUsers() {
        // 只有管理员可以访问
        return ResponseEntity.ok("Admin data");
    }
}

测试示例

public class JwtTest {
    public static void main(String[] args) {
        // 1. 生成令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put("role", "ADMIN");
        claims.put("email", "admin@example.com");
        String token = JwtUtil.generateToken("admin", claims);
        System.out.println("生成的Token: " + token);
        // 2. 验证令牌
        boolean isValid = JwtUtil.validateToken(token);
        System.out.println("令牌是否有效: " + isValid);
        // 3. 解析令牌
        Claims parsedClaims = JwtUtil.parseToken(token);
        System.out.println("用户名: " + parsedClaims.getSubject());
        System.out.println("角色: " + parsedClaims.get("role"));
        System.out.println("邮箱: " + parsedClaims.get("email"));
        System.out.println("过期时间: " + parsedClaims.getExpiration());
        // 4. 获取用户名
        String username = JwtUtil.getUsernameFromToken(token);
        System.out.println("从Token获取的用户名: " + username);
        // 5. 检查是否过期
        boolean isExpired = JwtUtil.isTokenExpired(token);
        System.out.println("是否过期: " + isExpired);
    }
}

使用Interceptor方式(不依赖Spring Security)

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JwtInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception {
        // 放行登录请求
        if (request.getRequestURI().contains("/api/auth/login")) {
            return true;
        }
        String token = extractToken(request);
        if (token == null || !JwtUtil.validateToken(token)) {
            response.setStatus(401);
            response.getWriter().write("{\"error\": \"未授权访问\"}");
            return false;
        }
        // 将用户信息设置到请求属性中
        String username = JwtUtil.getUsernameFromToken(token);
        request.setAttribute("username", username);
        return true;
    }
    private String extractToken(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

前端调用示例

// 登录
async function login() {
    const response = await fetch('/api/auth/login', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            username: 'admin',
            password: 'admin123'
        })
    });
    const data = await response.json();
    const token = data.token;
    // 存储token到本地
    localStorage.setItem('jwt_token', token);
    // 后续请求携带token
    const profileResponse = await fetch('/api/profile', {
        headers: {
            'Authorization': `Bearer ${token}`
        }
    });
    const profile = await profileResponse.json();
    console.log(profile);
}

最佳实践建议

  1. 密钥管理:使用密钥管理服务,不要硬编码
  2. 令牌过期:设置合理的过期时间(通常15-30分钟)
  3. HTTPS:生产环境必须使用HTTPS
  4. 刷新机制:实现Token刷新机制
  5. 黑名单:考虑实现Token黑名单机制
  6. 日志记录:记录重要的认证事件

这个实现提供了完整的JWT认证流程,你可以根据具体需求进行调整和扩展。

抱歉,评论功能暂时关闭!