SpringBoot安全实战:集成JWT与OAuth2构建企业级认证系统

🎓博主介绍:Java、Python、js全栈开发 “多面手”,精通多种编程语言和技术,痴迷于人工智能领域。秉持着对技术的热爱与执着,持续探索创新,愿在此分享交流和学习,与大家共进步。
📖DeepSeek-行业融合之万象视界(附实战案例详解100+)
📖全栈开发环境搭建运行攻略:多语言一站式指南(环境搭建+运行+调试+发布+保姆级详解)
👉感兴趣的可以先收藏起来,希望帮助更多的人
在这里插入图片描述

SpringBoot安全实战:集成JWT与OAuth2构建企业级认证系统

一、引言

在当今数字化的时代,企业级应用系统的安全性至关重要。用户认证与授权作为系统安全的第一道防线,直接关系到用户数据的安全和系统的稳定运行。Spring Boot作为一个广泛应用的Java开发框架,提供了丰富的安全特性。而JWT(JSON Web Token)和OAuth2则是目前在认证与授权领域被广泛使用的技术。本文将详细介绍如何在Spring Boot项目中集成JWT与OAuth2,构建一个企业级的认证系统。

二、JWT与OAuth2简介

2.1 JWT简介

JWT是一种用于在网络应用间安全传递声明的开放标准(RFC 7519)。它由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。头部包含了令牌的类型和使用的签名算法;载荷包含了声明,声明是关于实体(通常是用户)和其他数据的声明;签名则是用于验证消息在传递过程中没有被更改。

JWT的优点包括:

  • 无状态:不依赖于服务器端的会话状态,适合于分布式系统。
  • 可扩展性:可以在载荷中添加自定义的声明。
  • 跨域支持:可以在不同的域名之间传递。

2.2 OAuth2简介

OAuth2是一种授权框架,用于允许用户授权第三方应用访问其在另一个服务提供商上的资源,而无需共享其凭证。OAuth2定义了四种授权模式:授权码模式(Authorization Code)、简化模式(Implicit)、密码模式(Resource Owner Password Credentials)和客户端模式(Client Credentials)。

OAuth2的优点包括:

  • 安全性高:通过令牌进行授权,避免了直接传递用户的敏感信息。
  • 灵活性强:支持多种授权模式,可以根据不同的应用场景选择合适的模式。

三、Spring Boot项目搭建

3.1 创建Spring Boot项目

可以使用Spring Initializr(https://start.spring.io/)来创建一个新的Spring Boot项目。在创建项目时,需要添加以下依赖:

  • Spring Web
  • Spring Security
  • Spring Boot DevTools

3.2 项目结构

创建好项目后,项目的基本结构如下:

src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           └── demo
│   │               ├── DemoApplication.java
│   │               ├── controller
│   │               │   └── UserController.java
│   │               ├── service
│   │               │   └── UserService.java
│   │               └── security
│   │                   ├── JwtAuthenticationFilter.java
│   │                   ├── JwtAuthorizationFilter.java
│   │                   ├── JwtTokenProvider.java
│   │                   └── SecurityConfig.java
│   └── resources
│       ├── application.properties
│       └── static
│           └── index.html
└── test
    └── java
        └── com
            └── example
                └── demo
                    └── DemoApplicationTests.java

四、集成JWT

4.1 添加JWT依赖

pom.xml中添加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>

4.2 创建JWT工具类

创建一个JwtTokenProvider类,用于生成和验证JWT令牌:

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

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

@Component
public class JwtTokenProvider {

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

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

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return doGenerateToken(claims, userDetails.getUsername());
    }

    private String doGenerateToken(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.HS512, secret)
               .compact();
    }

    public boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    public String getUsernameFromToken(String token) {
        final Claims claims = getAllClaimsFromToken(token);
        return claims.getSubject();
    }

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

    private boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Date getExpirationDateFromToken(String token) {
        final Claims claims = getAllClaimsFromToken(token);
        return claims.getExpiration();
    }
}

4.3 创建JWT过滤器

创建JwtAuthenticationFilterJwtAuthorizationFilter类,用于处理JWT的认证和授权:

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 JwtAuthorizationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;
    private final UserDetailsService userDetailsService;

    public JwtAuthorizationFilter(JwtTokenProvider jwtTokenProvider, UserDetailsService userDetailsService) {
        this.jwtTokenProvider = jwtTokenProvider;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String header = request.getHeader("Authorization");
        String username = null;
        String authToken = null;
        if (header != null && header.startsWith("Bearer ")) {
            authToken = header.replace("Bearer ", "");
            try {
                username = jwtTokenProvider.getUsernameFromToken(authToken);
            } catch (Exception e) {
                logger.error("Error getting username from token", e);
            }
        } else {
            logger.warn("Couldn't find bearer string, will ignore the header");
        }
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if (jwtTokenProvider.validateToken(authToken, userDetails)) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                logger.info("authenticated user " + username + ", setting security context");
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(request, response);
    }
}

五、集成OAuth2

5.1 添加OAuth2依赖

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

5.2 配置OAuth2客户端

application.properties中配置OAuth2客户端信息:

spring.security.oauth2.client.registration.google.client-id=your-client-id
spring.security.oauth2.client.registration.google.client-secret=your-client-secret
spring.security.oauth2.client.registration.google.scope=openid,profile,email
spring.security.oauth2.client.provider.google.issuer-uri=https://accounts.google.com

5.3 配置OAuth2资源服务器

创建一个SecurityConfig类,配置OAuth2资源服务器:

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.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
           .authorizeRequests()
               .antMatchers("/public/**").permitAll()
               .anyRequest().authenticated()
               .and()
           .oauth2Login()
               .and()
           .oauth2ResourceServer()
               .jwt();
        return http.build();
    }
}

六、构建企业级认证系统

6.1 用户认证流程

  1. 用户在客户端输入用户名和密码,发送登录请求到服务器。
  2. 服务器接收到请求后,验证用户名和密码。
  3. 如果验证通过,服务器使用JwtTokenProvider生成JWT令牌,并返回给客户端。
  4. 客户端将JWT令牌存储在本地,后续的请求都在请求头中携带该令牌。

6.2 用户授权流程

  1. 客户端发送请求到服务器,请求头中携带JWT令牌。
  2. 服务器接收到请求后,使用JwtAuthorizationFilter验证JWT令牌的有效性。
  3. 如果令牌有效,服务器从令牌中获取用户信息,并进行授权检查。
  4. 如果授权通过,服务器处理请求并返回响应;否则,返回403 Forbidden错误。

6.3 集成OAuth2认证

  1. 用户在客户端点击第三方登录按钮,客户端重定向到第三方认证服务器。
  2. 用户在第三方认证服务器上进行登录和授权。
  3. 第三方认证服务器返回授权码给客户端。
  4. 客户端使用授权码向第三方认证服务器换取访问令牌。
  5. 客户端将访问令牌发送到服务器,服务器使用OAuth2ResourceServer验证访问令牌的有效性。
  6. 如果令牌有效,服务器从令牌中获取用户信息,并进行授权检查。
  7. 如果授权通过,服务器处理请求并返回响应;否则,返回403 Forbidden错误。

七、总结

通过本文的介绍,我们详细了解了如何在Spring Boot项目中集成JWT与OAuth2,构建一个企业级的认证系统。JWT提供了一种无状态的认证方式,适合于分布式系统;OAuth2则提供了一种安全的授权机制,允许用户授权第三方应用访问其资源。通过将两者结合使用,可以提高系统的安全性和可扩展性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

fanxbl957

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

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

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

打赏作者

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

抵扣说明:

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

余额充值