说明: 学习本节前先复习以下文章。
SpringBoot电商项目购物车模块统一校验当前是否有用户登录、SpringCloud之Feign携带session信息
前面开发完了Gateway网关,现在开始将Gateway网关应用到我们的SpringCloud电商项目中,将zuul升级为gateway。。。
zuul升级为gateway
说明: 我们首先知道的是,我们使用了JWT认证,所以将以前的session认证全部取消掉,改为jwt企业级认证。
我们一个个模块来看,首先看用户模块:
排除依赖
去除注解
去除配置文件
上面的操作是其它模块也要做的,跟着此模块操作即可。
查找相关session代码
接下来修改session有关代码,升级为jwt.
首先是Controller层
说明:
相关线程池的使用参考SpringCloud之线程池和Threadlocal在项目中的应用
配置过滤器与实现
先查看下两者之间关系:
package com.imooc.cloud.mall.practice.user.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 描述: User过滤器的配置
*/
@Configuration
public class UserFilterConfig {
@Bean
public UserInfoFilter userInfoInterceptor() {
return new UserInfoFilter();
}
@Bean(name = "userFilterConf")
public FilterRegistrationBean userFilterConfig() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(userInfoInterceptor());
filterRegistrationBean.addUrlPatterns("/cart/*");
filterRegistrationBean.addUrlPatterns("/order/*");
filterRegistrationBean.addUrlPatterns("/user/update");
filterRegistrationBean.setName("userFilterConf");
return filterRegistrationBean;
}
}
先查看:
SpringBoot系列教程web篇之过滤器Filter使用指南 - 腾讯云开发者社区-腾讯云
接下来实现过滤器:
package com.imooc.cloud.mall.practice.user.filter;
import com.imooc.cloud.mall.practice.common.common.Constant;
import com.imooc.cloud.mall.practice.user.model.pojo.User;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 把Header里的用户信息,放到本地的ThreadLocal里
*/
@Configuration
@EnableFeignClients
public class UserInfoFilter implements Filter {
public static ThreadLocal<User> userThreadLocal = new ThreadLocal();
public User currentUser = new User();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//通过RequestContextHolder获取本地请求
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return;
}
//获取本地线程绑定的请求对象
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
//给请求模板附加本地线程头部信息,把User信息放到ThreadLocal里
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
String value = values.nextElement();
if (name.equals(Constant.USER_ID)) {
currentUser.setId(Integer.valueOf(value));
}
if (name.equals(Constant.USER_NAME)) {
currentUser.setUsername(value);
}
if (name.equals(Constant.USER_ROLE)) {
currentUser.setRole(Integer.valueOf(value));
}
//必须保证User的3个字段都完整,否则意味着网关传递信息时出错了
if (currentUser.getId() != null && currentUser.getUsername() != null && currentUser.getRole() != null) {
userThreadLocal.set(currentUser);
}
}
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
Filter,过滤器,属于Servlet规范,并不是Spring独有的。其作用从命名上也可以看出一二,拦截一个请求,做一些业务逻辑操作,然后可以决定请求是否可以继续往下分发,落到其他的Filter或者对应的Servlet
简单描述下一个http请求过来之后,一个Filter的工作流程:
- 首先进入filter,执行相关业务逻辑
- 若判定通行,则进入Servlet逻辑,Servlet执行完毕之后,又返回Filter,最后在返回给请求方
- 判定失败,直接返回,不需要将请求发给Servlet
过滤器里面的三个方法 init : filter对象只会创建一次,init方法也只会执行一次。 doFilter : 主要的业务代码编写方法,可以多次重复调用 destroy : 在销毁Filter时自动调用(程序关闭或者主动销毁Filter)。
其它模块按照上面方法配置
首先是商品模块
删除依赖配置注解后,由于每个模块是独立服务的,所以需要Filter,将用户模块的Filter直接复制过来即可生效。。。
其次是订单模块
继续检查:
同理还有product的pojo类。。。
疑惑? 为了解决filter的耦合问题,把其它两个模块依赖删了,从而导入了其它模块的类,我寻思这不是因小失大,耦合更加严重了?
这里保留意见,而且这里filter每个模块都一样,都是过滤出用户信息,为何不直接放在公共模块,是不是这里只是比较特殊,每个模块其实有些项目不止用到用户信息过滤,还有其它信息,所以每个模块需要一个自己的过滤。。。
测试
启动Gateway网关,不启动zuul…
作业,实现获取JWT
参考: 项目实战生成JWT
其它测试就不放上来了,如果403就需要添加jwt_token。。。