0. 整体流程(先看这个,你就全懂)
-
前端用户登录,登陆成功后会返回Token给前端,前端要进行保存。
-
前端 每次请求在请求头里带 Token →
Authorization: Bearer xxxxx -
网关 Gateway 用 GlobalFilter 全局过滤器:
- 拿到 Token
- 校验、解析出 userId
- 重新构造请求头 →
user-info: 1001 - 转发给下游微服务(user-service /item-service 等)
-
每个微服务(比如 B 服务) 用 SpringMVC 的 preHandle 拦截器:
- 从请求头拿到
user-info - 存入
UserContext - 业务代码直接用:
UserContext.getUser()
- 从请求头拿到
-
微服务之间互相调用(A → B) 用 Feign 拦截器:
- 自动把
user-info带到下一个服务 - 全程不用你手动传参
- 自动把
1. 公共模块(common)通用代码
1.1 UserContext.java(A、B、网关、所有服务都用)
java
package com.hmall.common.utils;
public class UserContext {
private static final ThreadLocal<Long> TL = new ThreadLocal<>();
public static void setUser(Long userId) {
TL.set(userId);
}
public static Long getUser() {
return TL.get();
}
public static void remove() {
TL.remove();
}
}
1.2 JWT 工具类(生成、解析 Token)
java
package com.hmall.common.utils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.Map;
public class JwtTool {
// 密钥,实际项目放配置文件
private static final String SECRET = "your-32-byte-long-secret-key-here!!!!";
private static final SecretKey KEY = Keys.hmacShaKeyFor(SECRET.getBytes());
private static final long EXPIRATION = 1000 * 60 * 60 * 24 * 7; // 7天
// 生成Token
public static String createToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(KEY, SignatureAlgorithm.HS256)
.compact();
}
// 解析Token
public static Claims parseToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(KEY)
.build()
.parseClaimsJws(token)
.getBody();
}
}
2. 登录接口(auth-service /user-service)
2.1 登录成功生成 Token
java
@PostMapping("/login")
public Result<String> login(@RequestBody LoginDTO dto) {
// 1. 校验用户名密码
User user = userService.lambdaQuery()
.eq(User::getPhone, dto.getPhone())
.one();
if (user == null || !passwordMatch(user.getPassword(), dto.getPassword())) {
return Result.error("账号或密码错误");
}
// 2. 生成Token
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
String token = JwtTool.createToken(claims);
return Result.success(token);
}
3. 网关 Gateway 解析 Token(最重要!)
3.1 网关过滤器:校验 Token → 传递 userId
java
package com.hmall.gateway.filter;
import com.hmall.common.utils.JwtTool;
import io.jsonwebtoken.Claims;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class AuthFilter implements GlobalFilter, Ordered {
private static final String[] ALLOW_PATHS = {
"/login", "/register", "/search", "/test"
};
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().toString();
// 1. 白名单直接放行
for (String allow : ALLOW_PATHS) {
if (path.contains(allow)) {
return chain.filter(exchange);
}
}
// 2. 获取Token
HttpHeaders headers = request.getHeaders();
String token = headers.getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
return unauthorized(exchange);
}
token = token.substring(7);
// 3. 解析Token
Claims claims;
try {
claims = JwtTool.parseToken(token);
} catch (Exception e) {
return unauthorized(exchange);
}
// 4. 拿到userId,放进请求头传给下游微服务
Long userId = claims.get("userId", Long.class);
ServerHttpRequest newRequest = request.mutate()
.header("user-info", userId.toString())
.build();
return chain.filter(exchange.mutate().request(newRequest).build());
}
private Mono<Void> unauthorized(ServerWebExchange exchange) {
exchange.getResponse().setStatusCode(org.springframework.http.HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -100;
}
}
4. 你之前那套:Feign 透传(A 调 B 自动带 userId)
4.1 Feign 拦截器(A 服务 → 自动传)
java
package com.hmall.api.config;
import com.hmall.common.utils.UserContext;
import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DefaultFeignConfig {
@Bean
public RequestInterceptor userInfoInterceptor() {
return template -> {
Long userId = UserContext.getUser();
if (userId != null) {
template.header("user-info", userId.toString());
}
};
}
}
4.2 微服务接收(B 服务 → 自动收)
拦截器
java
package com.hmall.item.interceptor;
import com.hmall.common.utils.UserContext;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UserInfoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String userIdStr = request.getHeader("user-info");
if (userIdStr != null) {
UserContext.setUser(Long.valueOf(userIdStr));
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
UserContext.remove();
}
}
注册拦截器
java
package com.hmall.item.config;
import com.hmall.item.interceptor.UserInfoInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserInfoInterceptor())
.addPathPatterns("/**");
}
}
5. 最终使用(任何微服务直接用)
java
Long userId = UserContext.getUser();
6. 一句话总结你现在的体系
✅ 登录生成 Token
✅ 网关 解析 Token → 拿 userId
✅ 网关把 userId 放入请求头
✅ Feign 自动 透传到所有微服务
✅ 任何服务直接 UserContext.getUser() 拿用户
这就是标准企业级微服务登录鉴权体系。
前端带 token → 网关 GlobalFilter 解析 token → 放入 user-info 请求头 → 传给微服务 → 微服务用 preHandle 接收 user-info → Feign 自动透传。