什么是JWT?SpringBoot如何整合JWT实现安全认证?

106 阅读3分钟

JWT介绍

JWT(JSON Web Token)是一种基于JSON的开放标准(RFC 7519),用于在网络上安全地传输声明(claims)。JWT可以被用于认证和信息交换,通常用于前后端之间的身份验证和授权。

JWT 由三部分组成,分别如下

Header(头部)

包含了JWT的类型(typ)和使用的签名算法(alg)等信息,通常由两部分组成,第一部分是JWT的类型("typ")和使用的签名算法("alg")。如下所示。

{
  "typ": "JWT",
  "alg": "HS256"
}

上面的示例表示 JWT的类型为 "JWT",使用的签名算法是 HMAC SHA-256(HS256)

Payload(载荷)

包含了需要传输的信息,称为声明(claims),声明可以是标准声明(registered claims)、公共声明(public claims)和私有声明(private claims)。标准声明包括了一些预定义的声明,如下所示。

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

上面的示例中,sub表示主题(subject),name表示姓名,admin表示是否是管理员。

Signature(签名)

使用Base64编码的Header和Payload连接在一起,然后加上一个密钥,通过指定的算法生成签名,用于验证消息的完整性和真实性。

JWT主要用于身份认证和授权,用户在登录成功后,服务端会生成一个JWT并返回给客户端,客户端在后续的请求中带上JWT,服务端验证JWT的合法性,如果验证通过则认为用户已经登录。常见的场景包括单点登录(SSO)、跨域认证、微服务架构中的用户身份传递等。

SpringBoot如何整合JWT?

在Spring Boot中整合JWT需要进行以下步骤

  1. 添加依赖:在pom.xml文件中添加 JWT 相关的依赖。
  2. 创建 JWT 工具类:编写一个工具类来生成 JWT、解析 JWT 等操作。
  3. 创建认证过滤器:编写一个认证过滤器,在用户登录成功后生成 JWT,并在请求中验证 JWT 的合法性。
  4. 配置 Spring Security:配置 Spring Security 来定义用户认证和授权的规则。

添加POM依赖

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

创建工具类

编写一个工具类来生成 JWT、解析 JWT 等操作。

@Component
public class JwtUtils {

    private static final String SECRET_KEY = "your-secret-key"; // 用于生成签名的密钥
    private static final long EXPIRATION_TIME = 864_000_000; // JWT 有效期为 10 天

    public String generateToken(String username) {
        Key key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
        return Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(key)
                .compact();
    }

    public String getUsernameFromToken(String token) {
        Claims claims = Jwts.parserBuilder()
                .setSigningKey(SECRET_KEY.getBytes())
                .build()
                .parseClaimsJws(token)
                .getBody();
        return claims.getSubject();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder()
                    .setSigningKey(SECRET_KEY.getBytes())
                    .build()
                    .parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

认证过滤器

编写一个认证过滤器,在用户登录成功后生成 JWT,并在请求中验证 JWT 的合法性。

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;
    private final JwtUtils jwtUtils;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtUtils jwtUtils) {
        this.authenticationManager = authenticationManager;
        this.jwtUtils = jwtUtils;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {
        String username = (String) authResult.getPrincipal();
        String token = jwtUtils.generateToken(username);
        response.addHeader("Authorization", "Bearer " + token);
    }
}

整合SpringSecurity

在Spring Boot配置类中配置Spring Security。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private UserDetailsServiceImpl userDetailsService;
    @Resource
    private JwtUtils jwtUtils;

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

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance(); // 不对密码进行加密处理
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/api/authenticate").permitAll() // 放行登录接口
                .anyRequest().authenticated(); // 其他请求需要认证
        http.addFilterBefore(new JwtAuthenticationFilter(authenticationManager(), jwtUtils),
                UsernamePasswordAuthenticationFilter.class);
    }
}

在这个示例中,我们定义了一个简单的SpringBoot应用,并使用了SpringSecurity进行用户认证。JwtAuthenticationFilter继承了SpringSecurity提供的
UsernamePasswordAuthenticationFilter,并在用户登录成功后生成 JWT。

SecurityConfig 类配置了SpringSecurity,定义了用户认证规则,并添加了JwtAuthenticationFilter过滤器来验证JWT的合法性。JwtUtils类则封装了JWT的生成、解析和验证方法。

需要注意的是,在实际项目中,需要根据实际情况来配置用户认证、授权规则,并采用安全的密码加密方式。