Spring Boot怎么集成JWT实现前后端认证

发布时间:2022-04-13 10:09:49 作者:iii
来源:亿速云 阅读:220

Spring Boot怎么集成JWT实现前后端认证

目录

  1. 引言
  2. JWT简介
  3. Spring Boot集成JWT的准备工作
  4. JWT的生成与验证
  5. Spring Security集成JWT
  6. 前后端分离的认证流程
  7. JWT的刷新机制
  8. JWT的安全性考虑
  9. 总结

引言

在现代Web应用中,前后端分离的架构越来越流行。这种架构下,前端和后端通过API进行通信,因此需要一个可靠的认证机制来确保通信的安全性。JSON Web Token(JWT)是一种轻量级的认证机制,广泛应用于前后端分离的应用中。本文将详细介绍如何在Spring Boot项目中集成JWT,实现前后端的认证。

JWT简介

什么是JWT

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

JWT的结构

JWT的结构由三部分组成,用点(.)分隔:

  1. Header:包含令牌的类型(即JWT)和所使用的签名算法(如HMAC SHA256或RSA)。
  2. Payload:包含声明(claims)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型:注册声明、公共声明和私有声明。
  3. Signature:用于验证消息在传输过程中没有被篡改。签名是通过将编码后的头部、编码后的载荷和一个密钥进行签名生成的。

JWT的优点

Spring Boot集成JWT的准备工作

创建Spring Boot项目

首先,我们需要创建一个Spring Boot项目。可以使用Spring Initializr来快速生成项目骨架。

  1. 打开Spring Initializr
  2. 选择项目类型为Maven Project,语言为Java,Spring Boot版本选择最新的稳定版。
  3. 添加依赖:Spring Web、Spring Security、Spring Data JPA、H2 Database(用于测试)。
  4. 点击“Generate”按钮下载项目。

添加依赖

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

<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

在Spring Boot中,我们可以使用io.jsonwebtoken库来生成JWT。以下是一个简单的JWT生成示例:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;

public class JwtUtil {

    private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    private static final long EXPIRATION_TIME = 864_000_000; // 10 days

    public static String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SECRET_KEY)
                .compact();
    }
}

验证JWT

验证JWT的过程与生成JWT类似,我们需要使用相同的密钥来验证签名:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;

public class JwtUtil {

    public static String validateToken(String token) {
        Jws<Claims> claimsJws = Jwts.parserBuilder()
                .setSigningKey(SECRET_KEY)
                .build()
                .parseClaimsJws(token);
        return claimsJws.getBody().getSubject();
    }
}

Spring Security集成JWT

配置Spring Security

在Spring Boot中,我们可以通过配置Spring Security来保护我们的API。以下是一个简单的Spring Security配置:

import org.springframework.context.annotation.Bean;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/public/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilter(new JwtAuthenticationFilter(authenticationManager()))
            .addFilter(new JwtAuthorizationFilter(authenticationManager()));
    }

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

自定义UserDetailsService

我们需要自定义一个UserDetailsService来加载用户信息:

import org.springframework.security.core.userdetails.User;
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;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 这里可以从数据库中加载用户信息
        return User.withUsername(username)
                .password("password")
                .authorities("ROLE_USER")
                .build();
    }
}

JWT过滤器

我们需要创建两个过滤器:一个用于处理登录请求并生成JWT,另一个用于验证JWT并授权用户访问受保护的资源。

JwtAuthenticationFilter

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;

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

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            UserCredentials credentials = new ObjectMapper().readValue(request.getInputStream(), UserCredentials.class);
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            credentials.getUsername(),
                            credentials.getPassword(),
                            new ArrayList<>())
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) {
        String token = JwtUtil.generateToken(authResult.getName());
        response.addHeader("Authorization", "Bearer " + token);
    }
}

JwtAuthorizationFilter

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

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

        if (header == null || !header.startsWith("Bearer ")) {
            chain.doFilter(request, response);
            return;
        }

        String token = header.replace("Bearer ", "");
        String username = JwtUtil.validateToken(token);

        if (username != null) {
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        chain.doFilter(request, response);
    }
}

前后端分离的认证流程

用户登录

  1. 前端发送登录请求,包含用户名和密码。
  2. 后端验证用户名和密码,生成JWT并返回给前端。
  3. 前端将JWT存储在本地(如localStorage或sessionStorage)。

访问受保护资源

  1. 前端在每次请求时,将JWT放在请求头中(如Authorization: Bearer <token>)。
  2. 后端验证JWT的有效性,如果有效则允许访问受保护的资源。

JWT的刷新机制

刷新Token

JWT的有效期通常较短,为了保持用户的登录状态,我们需要实现一个刷新Token的机制。当JWT即将过期时,前端可以请求一个新的JWT。

刷新Token的实现

  1. 前端在JWT即将过期时,发送刷新Token的请求。
  2. 后端验证旧的JWT,如果有效则生成一个新的JWT并返回给前端。
  3. 前端使用新的JWT替换旧的JWT。

JWT的安全性考虑

JWT的安全性

JWT的最佳实践

总结

本文详细介绍了如何在Spring Boot项目中集成JWT,实现前后端的认证。通过JWT,我们可以实现无状态的认证机制,适用于前后端分离的架构。同时,我们还讨论了JWT的安全性考虑和最佳实践,以确保应用的安全性。希望本文能帮助你更好地理解和应用JWT。

推荐阅读:
  1. 基于Spring Boot+Spring Security+JWT+Vue前后端分离的开源项目
  2. Spring Boot集成Spring Security

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

springboot jwt

上一篇:Sharding-Proxy分库分表和数据加密怎么实现

下一篇:Dockerfile文件编写及构建镜像命令是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》