**
微服务之间如何通过网关,ThreadLocal ,拦截器,过滤器 共享用户登录信息(例如id token信息)
**
1.在编写登录逻辑时,使用id去生成一个token
@PostMapping("/login")
// @RequestParam(required = false) String username, @RequestParam(required=false) String password
public Result<Map> login(@RequestBody User user) {
Map<String, Object> map = new HashMap<>();
try {
User users = userService.login(user.getUsername(), user.getPassword());
//生成JWT的令牌
long id = users.getId().longValue();
String token = AppJwtUtil.getToken(id);
map.put("id",users.getId());
map.put("state", true);
map.put("msg", "认证成功");
map.put("token", token);//响应token
return Result.success(map);
} catch (Exception e) {
map.put("state", false);
map.put("msg", e.getMessage());
return Result.error(map);
}
}
2.在网关中添加filter过滤器,调用jwt工具解析token,把解析后的用户信息存储到header(一定要加上component注解)
package com.isoft.mygateway.filter;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.isoft.mygateway.utils.AppJwtUtil;
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.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class AuthorizeFilter implements Ordered, GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取request和response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//2.判断是否是登录
if (request.getURI().getPath().contains("/login")) {
//放行
return chain.filter(exchange);
}
//3.获取token
String token = request.getHeaders().getFirst("token");
//4.判断token是否存在
if (StringUtils.isBlank(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//5.判断token是否有效
try {
Claims claimsBody = AppJwtUtil.getClaimsBody(token);
//是否是过期
int result = AppJwtUtil.verifyToken(claimsBody);
if (result == 1 || result == 2) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 网关进行token解析后,把解析后的用户信息存储到header
Object userId = claimsBody.get("id"); //这里的id 是 登录时获取token的id
// 在header中添加新的信息
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
httpHeaders.add("userId", userId + "");
}).build();
// 重置header
exchange.mutate().request(serverHttpRequest).build();
} catch (Exception e) {
e.printStackTrace();
}
//6.放行
return chain.filter(exchange);
}
/**
* 优先级设置 值越小 优先级越高
*
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
3.在需要使用到用户信息的微服务 中使用拦截器 获取上一步中保存到请求头中的用户信息,并将其保存到ThreadLocal中(注意一定加@Component ,这里我没加找了半天错!!!真的栓q)
package com.isoft.mymodule.interceptor;
import com.heima.utils.common.BaseContext;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.C;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userId = request.getHeader("userId");
Optional<String> optional = Optional.ofNullable(userId);
if (optional.isPresent()) {
// 把id存入threadLocal
Long id = Long.valueOf(userId);
BaseContext.setCurrentId(id);
log.info("设置用户信息到ThreadLocal中:{}", userId);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("清理threadLocal");
//一定要清理,要不然可能会发生内存泄漏
BaseContext.clear();
}
}
4.在当前微服务中配置3中的拦截器
package com.isoft.mymodule.config;
import com.isoft.mymodule.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
//配置类
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor()).addPathPatterns("/**");
}
//映射静态资源目录
// @Override
// protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
// registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/");
// }
}
5.这样就ok,进行测试 ,编写的接口如下:
@GetMapping("/showMyContract")
public Result<MyContract> showMyContract() {
MyContract myContract = myContractService.getById(BaseContext.getCurrentId());
return Result.success(myContract);
}
6.测试结果
首先登录,获取token
我的模块 微服务 查询操作