Java Web Spring Boot 中 JWT 令牌

Java Web Spring Boot 中 JWT 令牌相关内容

1. JWT 简介

JWT(JSON Web Token) 是一种开放标准(RFC 7519),用于在网络应用间安全地传输信息。JWT 通常用于身份验证和信息交换。JWT 由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),这三部分通过.分隔。

  • Header:包含令牌的类型(JWT)和所使用的签名算法(如 HMAC SHA256 或 RSA)。
  • Payload:包含声明(claims),声明是关于实体(通常是用户)和其他数据的声明。
  • Signature:用于验证消息在传输过程中没有被更改,并且,如果使用私钥签名的话,还可以验证 JWT 的发送方是否是它所声称的发送方。
2. JWT 的优点
  • 无状态:JWT 自身包含了所有必要的信息,服务器不需要存储会话状态。
  • 跨域支持:JWT 可以在不同的域之间传递,适用于分布式系统。
  • 安全性:通过签名和加密,确保信息不被篡改。
3. JWT 的使用场景
  • 身份验证:用户登录后,服务器生成 JWT 返回给客户端,客户端在后续请求中携带 JWT 进行身份验证。
  • 信息交换:确保信息在传输过程中不被篡改。
4. Spring Boot 中集成 JWT
4.1 添加依赖

pom.xml 文件中添加 JWT 相关的依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
4.2 配置 JWT

创建一个配置类,用于生成和验证 JWT:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

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

@Component
public class JwtTokenUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long expiration;

    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, username);
    }

    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

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

    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 = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

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

application.properties 文件中配置 JWT 的密钥和过期时间:

jwt.secret=your_secret_key
jwt.expiration=86400

这段代码是一个用于生成、验证和解析 JWT(JSON Web Token)的工具类。它使用了 io.jsonwebtoken 库来处理 JWT 的创建和解析。下面是对代码中各个部分的详细解释:

类和字段
@Component
public class JwtTokenUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long expiration;
  • @Component:将这个类标记为一个 Spring 组件,使其可以被 Spring 容器管理。
  • @Value("${jwt.secret}"):从配置文件中读取 jwt.secret 属性的值,并将其注入到 secret 字段中。
  • @Value("${jwt.expiration}"):从配置文件中读取 jwt.expiration 属性的值,并将其注入到 expiration 字段中。
生成 JWT
public String generateToken(String username) {
    Map<String, Object> claims = new HashMap<>();
    return createToken(claims, username);
}

private String createToken(Map<String, Object> claims, String subject) {
    return Jwts.builder()
            .setClaims(claims)
            .setSubject(subject)
            .setIssuedAt(new Date(System.currentTimeMillis()))
            .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
            .signWith(SignatureAlgorithm.HS256, secret)
            .compact();
}
  • generateToken(String username):生成一个 JWT,传入用户名作为参数。
  • createToken(Map<String, Object> claims, String subject):创建 JWT 的实际方法。
    • Jwts.builder():创建一个 JWT 构建器。
    • setClaims(claims):设置自定义声明(claims),这里是一个空 map。
    • setSubject(subject):设置 JWT 的主题(通常是用户名)。
    • setIssuedAt(new Date(System.currentTimeMillis())):设置 JWT 的签发时间。
    • setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)):设置 JWT 的过期时间。
    • signWith(SignatureAlgorithm.HS256, secret):使用 HS256 算法和密钥对 JWT 进行签名。
    • compact():生成最终的 JWT 字符串。
验证 JWT
public Boolean validateToken(String token, String username) {
    final String extractedUsername = extractUsername(token);
    return (extractedUsername.equals(username) && !isTokenExpired(token));
}
  • validateToken(String token, String username):验证 JWT 是否有效。
    • extractUsername(token):从 JWT 中提取用户名。
    • isTokenExpired(token):检查 JWT 是否过期。
    • 返回 true 如果用户名匹配且 JWT 未过期,否则返回 false
提取信息
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 = extractAllClaims(token);
    return claimsResolver.apply(claims);
}

private Claims extractAllClaims(String token) {
    return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}

private Boolean isTokenExpired(String token) {
    return extractExpiration(token).before(new Date());
}
  • extractUsername(String token):从 JWT 中提取用户名。
  • extractExpiration(String token):从 JWT 中提取过期时间。
  • extractClaim(String token, Function<Claims, T> claimsResolver):提取 JWT 中的声明。
  • extractAllClaims(String token):解析 JWT 并提取所有声明。
  • isTokenExpired(String token):检查 JWT 是否过期。
总结

这个 JwtTokenUtil 类提供了以下功能:

  1. 生成 JWT:根据用户名生成一个 JWT。
  2. 验证 JWT:验证 JWT 是否有效,包括检查用户名和过期时间。
  3. 提取信息:从 JWT 中提取用户名和过期时间。

通过这些方法,可以方便地在 Spring Boot 应用中使用 JWT 进行身份验证和信息交换。

4.3 创建过滤器

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

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.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
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @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 = jwtTokenUtil.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            if (jwtTokenUtil.validateToken(jwt, userDetails.getUsername())) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        chain.doFilter(request, response);
    }
}
4.4 配置 Spring Security

在 Spring Security 配置类中添加 JWT 过滤器:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService myUserDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests().antMatchers("/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 NoOpPasswordEncoder.getInstance();
    }
}
4.5 创建认证控制器

创建一个控制器,用于生成 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.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

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

        final UserDetails userDetails = userDetailsService
                .loadUserByUsername(authRequest.getUsername());

        final String jwt = jwtTokenUtil.generateToken(userDetails.getUsername());

        return ResponseEntity.ok(new AuthResponse(jwt));
    }
}
4.6 创建请求和响应类

创建请求和响应类,用于接收认证请求和返回 JWT:

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

    // Getters and Setters
}

public class AuthResponse {
    private final String jwt;

    public AuthResponse(String jwt) {
        this.jwt = jwt;
    }

    public String getJwt() {
        return jwt;
    }
}
5. 总结

通过以上步骤,我们完成了在 Spring Boot 中集成 JWT 的基本流程。主要包括以下几个方面:

  • 添加 JWT 依赖。
  • 配置 JWT 生成和验证工具类。
  • 创建过滤器拦截请求并验证 JWT。
  • 配置 Spring Security 以支持 JWT。
  • 创建认证控制器生成 JWT。

通过这些步骤,我们可以实现基于 JWT 的身份验证和信息交换,确保系统的安全性和无状态性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

需要重新演唱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值