SpringCloud實現權限管理(網關+jwt版)
首先要想一個問題:為什么微服務不能像普通的Spring Boot項目一樣鑒權?其實并不是不能,而是不適合。 在微服務架構中使用 Spring Security + RBAC/JET 時,會面臨 "重復鑒權" 的核心痛點。下面從技術原理到解決方案完整解析這個問題。
問題本質:為什么會有重復鑒權?
傳統單體架構流程

所有權限校驗集中在一個應用內完成 • 用戶登錄后,Session或Token在單應用中全局有效
微服務架構下的流程

每個微服務都需要獨立完成:
- 解析Token
- 查詢數據庫驗證權限
- 構建SecurityContext
假設有3個服務鏈式調用:
客戶端 → 網關 → 服務A → 服務B → 服務C 每個服務都重復: 查詢用戶數據(1次DB查詢) 查詢權限數據(1次DB查詢) ??總查詢次數 = 3服務 × 2查詢 = 6次? • 在服務交叉的時候,導致 多次數據庫查詢 和 重復計算,使數據庫壓力倍增和網絡開銷疊加。
(1)創(chuàng)建 JWT 工具類
在util模塊下創(chuàng)建JWT 工具類
@Component
public class JwtTokenUtil {
// 密鑰,用于簽名和驗證 JWT,應妥善保管
@Value("${jwt.secret}")
private String secret;
// JWT 的過期時間,這里設置為 10 小時
@Value("${jwt.expiration}")
private Long expiration;
// 根據用戶詳細信息生成 JWT
public String generateToken(SysRoleNameUserId sysRoleNameUserId) {
//自定義的聲明
Map<String, Object> claims = new HashMap<>();
//鑒權所需的權限角色
claims.put("identities",sysRoleNameUserId.getRoleNames());
return createToken(claims, String.valueOf(sysRoleNameUserId.getUserId()));
}
// 創(chuàng)建 JWT 的具體方法
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)//自定義的聲明
.setSubject(subject)//存的用戶id
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
//解析jwt
public Claims extractAllClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(secret)
.build()
.parseClaimsJws(token)
.getBody();
}
}
JWT 的密鑰(jwt.secret)應通過配置中心(如 Nacos)或環(huán)境變量注入,避免硬編碼。這里選擇環(huán)境變量注入: 在util模塊下的application.yml配置文件里面指定
jwt: secret: your-secret-key-here-must-be-at-least-256-bits-long expiration: 1000 * 60 * 60 * 10 # 10 hour
(2)創(chuàng)建JwtFilter
在gateway網關模塊創(chuàng)建JwtFilter過濾器來驗證并解析jwt,從而獲取權限信息
@Component
public class JwtFilter implements GlobalFilter, Ordered {
@Resource
private JwtTokenUtil jwtTokenUtil;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
List<String> authHeader = headers.get("Authorization");
if (authHeader != null && authHeader.get(0).startsWith("Bearer ")) {
String token = authHeader.get(0).substring(7);
// 驗證 JWT 并提取角色信息
Claims claims = null;
try{
claims = jwtTokenUtil.extractAllClaims(token);
}catch (IllegalArgumentException e) {
//解析 JWT 時發(fā)生其他錯誤
System.out.println("解析 token 時發(fā)生其他錯誤");
} catch (ExpiredJwtException e) {
//JWT 已過期
System.out.println("token 已過期");
}
//取出權限角色列表
Object identitiesObj = claims.get("identities");
List<GrantedAuthority> authorities = new ArrayList<>();
if (identitiesObj instanceof List<?>) {
for (Object role : (List<?>) identitiesObj) {
if (role instanceof String) {
// 將字符串轉換為 SimpleGrantedAuthority
authorities.add(new SimpleGrantedAuthority((String) role));
}
}
}
String userId = claims.getSubject();
//將 Authentication 對象設置到 SecurityContextHolder 中后,Spring Security 就能在后續(xù)的授權過程中使用這些權限信息了。
Authentication authentication = new UsernamePasswordAuthenticationToken(userId, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}
(3)配置 Spring Security
在網關gateway模塊配置所有微服務整體的權限管理規(guī)則:
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http.csrf().disable()// 關閉 CSRF(跨站請求偽造)防護。在無狀態(tài) API(如 JWT 場景)中,CSRF 防護不必要(通常依賴 Authorization 頭而非 Cookie)。 避免對 POST、PUT 等請求要求攜帶 CSRF Token。
.authorizeExchange()
.pathMatchers("/api/product/**").permitAll()
.pathMatchers("/admin/**").permitAll()
.anyExchange().authenticated();
return http.build();
}
}
如果各個微服務還需要獨自的更細粒度的權限控制,只需要在單個微服務模塊中單獨配置一個Spring Security就行了。
到此這篇關于SpringCloud實現權限管理(網關+jwt版)的文章就介紹到這了,更多相關SpringCloud 權限管理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java報錯:java.lang.UnsatisfiedLinkError問題的解決辦法
在Java開發(fā)中,java.lang.UnsatisfiedLinkError是一種與本地方法調用相關的常見異常,本文將詳細分析這一異常的背景、可能的原因、錯誤代碼示例、正確代碼示例,以及編寫代碼時需要注意的事項,需要的朋友可以參考下2024-09-09
Java線程隊列LinkedBlockingQueue的使用
本文主要介紹了Java線程隊列LinkedBlockingQueue的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-06-06

