概述
本文详细介绍了网关过滤器接入鉴权校验项目的搭建过程,涵盖了网关的基本概念、过滤器的作用、认证与鉴权的基础知识以及实战项目中的具体实现。通过该项目实战,读者可以深入了解如何在网关中进行身份认证、生成和验证Token,以及实现权限授权,最终完成网关过滤器接入鉴权校验项目实战。
1. 网关的基本概念
什么是网关
在分布式系统中,网关(Gateway)是一种用于转发请求的中间件组件,它位于客户端和后端服务之间。网关的主要职责是接收来自客户端的请求,根据预定义的规则将请求转发到适当的后端服务,并处理响应。网关还提供了其他功能,如负载均衡、缓存、安全认证等。
网关的作用
网关在网络架构中扮演着重要角色,具体作用包括:
- 请求路由:网关可以根据不同的请求路径或请求头信息将请求路由到不同的后端服务。
- 负载均衡:通过将请求分发到多个后端服务实例,实现请求的负载均衡,提高系统的可用性和性能。
- 缓存:网关可以缓存常见的请求,以减少后端服务的负担并提高响应速度。
- 安全过滤:网关可以对请求进行安全检查,如校验请求头、请求体等,以确保只有合法的请求能够到达后端服务。
- 协议转换:网关可以将一种协议的请求转换为另一种协议,以支持不同服务之间的通信。
- 监控与日志:网关可以记录请求和响应的详细信息,便于监控系统性能和追踪问题。
常见的网关类型
常见的网关类型包括:
- API网关:这是一种专门用于处理API请求的网关,它通常用于微服务架构中,负责将请求转发到相应的微服务。
- 协议网关:这类网关主要用于在不同的传输协议之间进行转换。例如,可以将HTTP请求转换为gRPC请求。
- 安全网关:这类网关专注于安全性,提供各种安全特性,如身份验证、授权、加密等。
- 边缘网关:这类网关放置在云服务和公网之间的边界,用于处理来自公网的流量,包括负载均衡、缓存和安全过滤等功能。
2. 过滤器的概念与作用
什么是过滤器
过滤器(Filter)是一种开发者可以自定义的处理逻辑模块,它用于在请求到达最终处理服务之前或之后对请求或响应进行处理。过滤器可以实现各种功能,如日志记录、错误处理、安全检查等。
过滤器在网关中的作用
在网关系统中,过滤器的作用主要有以下几点:
- 请求过滤:过滤器可以在请求到达后端服务之前对其进行预处理。例如,检查请求头、设置或修改请求参数等。
- 响应过滤:过滤器可以在响应从后端服务返回客户端之前对其进行处理。例如,修改响应头、加密响应内容等。
- 日志记录:过滤器可以记录请求和响应的详细信息,提供系统的监控和调试功能。
- 错误处理:过滤器可以捕获请求或响应中的错误,并提供相应的错误处理逻辑。
- 安全检查:过滤器可以执行各种安全检查,如身份验证、授权等,确保只有合法的请求才能到达后端服务。
示例:如何使用过滤器
以下是一个简单的Spring Cloud Gateway过滤器示例,演示了如何定义和使用过滤器:
- 定义过滤器
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
@Component
public class CustomFilter extends AbstractGatewayFilterFactory {
public CustomFilter() {
super(CustomFilter.class);
}
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
// 在请求到达后端服务之前执行的逻辑
System.out.println("Custom Filter Before Route");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 在请求从后端服务返回客户端之前执行的逻辑
System.out.println("Custom Filter After Route");
}));
};
}
}
- 配置过滤器
在Spring Cloud Gateway的application.yml
配置文件中启用自定义过滤器:
spring:
cloud:
gateway:
routes:
- id: custom_route
uri: http://example.com
predicates:
- Path=/path/*
filters:
- name: CustomFilter
3. 认证与鉴权基础知识
什么是鉴权和认证
- 身份认证(Authentication):身份认证是指验证用户的身份是否合法,通常通过检查用户提供的凭证(如用户名和密码)来实现。
- 权限授权(Authorization):权限授权是指验证用户是否有权访问特定资源或执行特定操作,即使用户已经通过了身份认证。
常见的鉴权方式
- 基本认证:这是一种简单的鉴权方式,通过在HTTP请求头中发送Base64编码的用户名和密码进行身份认证。
- Token认证:使用令牌(Token)进行身份认证,如JWT(JSON Web Token),在HTTP请求头中携带Token。
- OAuth2.0:这是一种广泛使用的开放授权协议,用于授权访问资源和服务。
- API密钥:为每个用户或应用程序分配一个唯一的密钥,用于认证和授权。
- 基于证书的认证:使用数字证书进行身份认证,通常用于需要高度安全性的场景。
如何进行鉴权
- 基本认证
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;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
- Token认证
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
@Configuration
public class SecurityConfig {
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("your-signing-key");
return converter;
}
}
- OAuth2.0
@Configuration
public class OAuth2Config {
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("your-signing-key");
return converter;
}
@Bean
public AuthorizationServerEndpointsConfigurer authorizationServerEndpointsConfigurer() {
return new AuthorizationServerEndpointsConfigurer()
.tokenStore(tokenStore())
.authenticationManager(authenticationManager());
}
@Bean
public AuthorizationServerSecurityConfigurer authorizationServerSecurityConfigurer() {
return new AuthorizationServerSecurityConfigurer("your-client-id");
}
@Bean
public AuthorizationServerConfigurerConfigurer authorizationServerConfigurer() {
return new AuthorizationServerConfigurerConfigurer() {
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
};
}
}
- API密钥
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;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/api/**").hasRole("API_USER")
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
- 基于证书的认证
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;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.x509()
.subjectPrincipalRegex("CN=(.*?), .*?")
.userDetailsService(userDetailsService());
}
@Bean
public UserDetailsService userDetailsService() {
return (username) -> {
if ("CN=user".equals(username)) {
return new User("user", "password".toCharArray(), true, true, true, true,
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
return null;
};
}
}
4. 实战项目搭建
准备开发环境
- 安装Java和Maven:确保已经安装了Java和Maven环境。Java版本建议使用Java 11及以上版本。
- 安装IDE:推荐使用IntelliJ IDEA或Eclipse作为开发工具。
- 配置本地仓库:确保Maven的本地仓库已配置正确。
创建项目结构
创建一个Spring Boot项目,项目结构如下:
gateway-filter-auth
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com.example.gateway
│ │ │ ├── GatewayApplication.java
│ │ │ └── config
│ │ │ ├── GatewayConfig.java
│ │ │ └── SecurityConfig.java
│ │ └── resources
│ │ └── application.yml
├── pom.xml
└── .gitignore
引入必要的库和工具
在pom.xml
文件中添加必要的依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
</dependencies>
5. 实现鉴权校验功能
设计鉴权流程
- 身份认证:客户端发送带有用户名和密码的HTTP请求到网关。
- Token生成:网关验证用户名和密码,如果成功,则生成一个JWT Token,并返回给客户端。
- Token验证:客户端在后续的请求中携带该Token访问资源,网关在接收到请求后验证Token的有效性。
- 权限授权:验证Token后,网关检查用户是否有权限访问请求的资源。
编写鉴权代码
- 创建Token生成器
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtTokenUtil {
private static final String SECRET = "your-secret-key";
private static final long EXPIRATION = 3600000;
public String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(new Date().getTime() + EXPIRATION))
.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) {
return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody().getSubject();
}
public boolean isTokenExpired(String token) {
final Date expiration = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody().getExpiration();
return expiration.before(new Date());
}
}
- 创建认证过滤器
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Configuration
public class GatewayConfig {
private final UserDetailsService userDetailsService;
private final JwtTokenUtil jwtTokenUtil;
public GatewayConfig(UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil) {
this.userDetailsService = userDetailsService;
this.jwtTokenUtil = jwtTokenUtil;
}
@Bean
public GatewayFilter authenticationFilter() {
return new OrderedGatewayFilter(new GlobalAuthenticationFilter(), 1);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p.path("/api/**")
.filters(f -> f.filter(authenticationFilter()))
.uri("http://localhost:8081"))
.build();
}
}
class GlobalAuthenticationFilter implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String authorization = exchange.getRequest().getHeaders().getFirst("Authorization");
if (authorization != null && authorization.startsWith("Bearer ")) {
final String token = authorization.substring(7);
try {
String username = jwtTokenUtil.getUsernameFromToken(token);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
authRequest.setDetails(new WebAuthenticationDetailsSource().buildDetails(exchange.getRequest()));
SecurityContextHolder.getContext().setAuthentication(authRequest);
} catch (Exception e) {
return Mono.fromRunnable(() -> exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED));
}
} else {
return Mono.fromRunnable(() -> exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED));
}
return chain.filter(exchange);
}
}
测试鉴权功能
为了测试鉴权功能,可以创建一个简单的用户服务,用于验证身份认证和授权。
- 创建用户服务
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 UserService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if ("user".equals(username)) {
return User.withDefaultPasswordEncoder()
.username(username)
.password("password")
.roles("USER")
.build();
}
throw new UsernameNotFoundException("User not found");
}
}
- 启动项目并测试
启动项目后,通过Postman或其他工具进行以下测试:
- 发送身份认证请求到网关,获取Token:
POST /authenticate
请求体:
{
"username": "user",
"password": "password"
}
- 使用获取到的Token访问受保护的资源:
GET /api/resource
请求头:
Authorization: Bearer <token>
6. 调试与部署
常见问题与解决方案
- Token生成失败:检查JWT库的配置是否正确,如密钥是否正确设置。
- Token验证失败:检查请求头中的Token是否正确,以及Token的有效性。
- 请求路由失败:确保路由配置正确,包括路径匹配和过滤器配置。
- 安全问题:确保所有敏感信息(如密钥)都已加密存储,并避免在代码中硬编码敏感信息。
如何部署项目
部署项目的步骤如下:
- 打包项目
mvn clean package
- 部署到服务器
将打包好的JAR文件上传到服务器,然后使用命令启动:
java -jar gateway-filter-auth.jar
监控与维护
为确保网关系统稳定运行,需要进行定期监控和维护:
- 监控系统性能:使用监控工具(如Prometheus、Grafana)监控系统的性能指标,如响应时间、吞吐量等。
- 日志记录与分析:记录详细的日志信息,并使用日志分析工具(如Logstash、Elasticsearch、Kibana)进行分析。
- 定期更新:保持系统组件和依赖库的更新,以获得最新的安全补丁和功能增强。
- 备份与恢复:定期备份系统配置和数据,并制定恢复计划,以防止数据丢失。
通过以上步骤,可以确保网关系统在生产环境中稳定、高效地运行。
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦