🎓博主介绍: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过滤器
创建JwtAuthenticationFilter和JwtAuthorizationFilter类,用于处理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 用户认证流程
- 用户在客户端输入用户名和密码,发送登录请求到服务器。
- 服务器接收到请求后,验证用户名和密码。
- 如果验证通过,服务器使用
JwtTokenProvider生成JWT令牌,并返回给客户端。 - 客户端将JWT令牌存储在本地,后续的请求都在请求头中携带该令牌。
6.2 用户授权流程
- 客户端发送请求到服务器,请求头中携带JWT令牌。
- 服务器接收到请求后,使用
JwtAuthorizationFilter验证JWT令牌的有效性。 - 如果令牌有效,服务器从令牌中获取用户信息,并进行授权检查。
- 如果授权通过,服务器处理请求并返回响应;否则,返回403 Forbidden错误。
6.3 集成OAuth2认证
- 用户在客户端点击第三方登录按钮,客户端重定向到第三方认证服务器。
- 用户在第三方认证服务器上进行登录和授权。
- 第三方认证服务器返回授权码给客户端。
- 客户端使用授权码向第三方认证服务器换取访问令牌。
- 客户端将访问令牌发送到服务器,服务器使用
OAuth2ResourceServer验证访问令牌的有效性。 - 如果令牌有效,服务器从令牌中获取用户信息,并进行授权检查。
- 如果授权通过,服务器处理请求并返回响应;否则,返回403 Forbidden错误。
七、总结
通过本文的介绍,我们详细了解了如何在Spring Boot项目中集成JWT与OAuth2,构建一个企业级的认证系统。JWT提供了一种无状态的认证方式,适合于分布式系统;OAuth2则提供了一种安全的授权机制,允许用户授权第三方应用访问其资源。通过将两者结合使用,可以提高系统的安全性和可扩展性。

1万+

被折叠的 条评论
为什么被折叠?



