🎓博主介绍:Java、Python、js全栈开发 “多面手”,精通多种编程语言和技术,痴迷于人工智能领域。秉持着对技术的热爱与执着,持续探索创新,愿在此分享交流和学习,与大家共进步。
📖全栈开发环境搭建运行攻略:多语言一站式指南(环境搭建+运行+调试+发布+保姆级详解)
👉感兴趣的可以先收藏起来,希望帮助更多的人
SpringBoot安全三剑客:Security+JWT+OAuth2全流程实现
一、引言
在当今的Web应用开发中,安全性是至关重要的一环。Spring Boot作为一款广泛使用的开发框架,为开发者提供了便捷的开发体验。而Spring Security、JWT(JSON Web Token)和OAuth2这三个技术,被称为Spring Boot安全三剑客,它们结合起来可以为Spring Boot应用提供全面、高效且安全的认证与授权解决方案。本文将详细介绍如何在Spring Boot应用中全流程实现这三个技术的整合。
二、Spring Security基础
2.1 Spring Security简介
Spring Security是一个强大且高度可定制的身份验证和访问控制框架,是Spring生态系统中的重要组成部分。它提供了全面的安全服务,包括基于表单的认证、HTTP基本认证、OAuth2等多种认证方式,以及基于角色的访问控制。
2.2 Spring Security在Spring Boot中的集成
在Spring Boot项目中集成Spring Security非常简单,只需要在pom.xml
中添加相应的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
添加依赖后,Spring Security会自动为应用添加基本的安全配置,所有的HTTP请求都需要进行身份验证。
2.3 自定义Spring Security配置
可以通过创建一个继承自WebSecurityConfigurerAdapter
的配置类来自定义Spring Security的配置:
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
在上述配置中,/public/**
路径下的请求不需要进行身份验证,其他请求则需要进行身份验证。同时,配置了表单登录和HTTP基本认证方式。
三、JWT基础
3.1 JWT简介
JWT(JSON Web Token)是一种用于在网络应用间安全传输信息的开放标准(RFC 7519)。它通常由三部分组成:头部(Header)、负载(Payload)和签名(Signature)。JWT以JSON对象的形式存储信息,并通过签名进行验证,确保信息的完整性和真实性。
3.2 JWT的工作原理
- 用户登录成功后,服务器生成一个JWT,并将其返回给客户端。
- 客户端在后续的请求中,将JWT包含在请求头中发送给服务器。
- 服务器接收到请求后,验证JWT的签名和有效性,如果验证通过,则允许用户访问受保护的资源。
3.3 在Spring Boot中集成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>
然后,创建一个JWT工具类来生成和验证JWT:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
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 JwtUtils {
private static final String SECRET_KEY = "your_secret_key";
private static final long EXPIRATION_TIME = 86400000; // 24 hours
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
public String extractUsername(String token) {
return extractClaims(token).getSubject();
}
private Claims extractClaims(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = extractClaims(token).getExpiration();
return expiration.before(new Date());
}
}
最后,在Spring Security配置中添加JWT过滤器:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
JWT过滤器的实现如下:
import io.jsonwebtoken.ExpiredJwtException;
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 JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtUtils.extractUsername(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtUtils.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
四、OAuth2基础
4.1 OAuth2简介
OAuth2是一种开放标准的授权协议,用于允许第三方应用代表用户访问受保护的资源。它提供了一种安全的方式,让用户可以授权第三方应用访问其在另一个服务提供商上的资源,而无需将自己的用户名和密码提供给第三方应用。
4.2 OAuth2的工作流程
OAuth2的工作流程主要包括以下几个步骤:
- 用户请求访问第三方应用的受保护资源。
- 第三方应用将用户重定向到授权服务器,请求用户授权。
- 用户在授权服务器上登录并授权第三方应用访问其资源。
- 授权服务器返回授权码给第三方应用。
- 第三方应用使用授权码向授权服务器换取访问令牌。
- 第三方应用使用访问令牌访问资源服务器上的受保护资源。
4.3 在Spring Boot中集成OAuth2
在pom.xml
中添加OAuth2相关的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
然后,在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.authorization-uri=https://accounts.google.com/o/oauth2/v2/auth
spring.security.oauth2.client.provider.google.token-uri=https://www.googleapis.com/oauth2/v4/token
spring.security.oauth2.client.provider.google.user-info-uri=https://www.googleapis.com/oauth2/v3/userinfo
spring.security.oauth2.client.provider.google.user-name-attribute=name
最后,在Spring Security配置中添加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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/oauth2/authorization/google"));
}
}
五、Spring Boot安全三剑客的整合
5.1 整体架构设计
将Spring Security、JWT和OAuth2整合在一起,可以构建一个强大的安全架构。Spring Security负责整体的安全配置和访问控制,JWT用于在客户端和服务器之间安全传输用户信息,OAuth2用于第三方应用的授权登录。
5.2 实现步骤
- 配置Spring Security,包括基本的认证和授权规则。
- 集成JWT,实现JWT的生成、验证和过滤。
- 集成OAuth2,配置OAuth2客户端信息和登录支持。
- 在控制器中处理不同的认证和授权逻辑。
5.3 示例代码
以下是一个简单的控制器示例,演示了如何处理不同的认证和授权逻辑:
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/public")
public String publicEndpoint() {
return "This is a public endpoint.";
}
@GetMapping("/protected")
@PreAuthorize("hasRole('USER')")
public String protectedEndpoint() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return "Hello, " + authentication.getName() + "! This is a protected endpoint.";
}
}
六、总结
通过Spring Security、JWT和OAuth2的整合,我们可以为Spring Boot应用提供全面、高效且安全的认证与授权解决方案。Spring Security提供了强大的安全配置和访问控制功能,JWT确保了用户信息的安全传输,OAuth2支持第三方应用的授权登录。在实际开发中,可以根据具体需求对这些技术进行灵活配置和扩展。