网关¶
在heima-leadnews-gateway导入以下依赖
pom文件
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- nacos的注册依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- nacos配置-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- jwt解析的jar包-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
在heima-leadnews-gateway下创建heima-leadnews-app-gateway微服务Module
在heima-leadnews-app-gateway|src|main|java|heima-leadnews-app-gateway
包下创建引导类:
package com.heima.app.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient //开启注册中心
public class AppGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(AppGatewayApplication.class,args);
}
}
在heima-leadnews-app-gateway|src|main|java|resource
中创建bootstrap.yml配置文件
server:
port: 51601
spring:
application:
name: leadnews-app-gateway
cloud:
nacos:
discovery:
#注册中心的地址
server-addr: 192.168.200.130:8848
config:
#配置中心的地址
server-addr: 192.168.200.130:8848
file-extension: yml
在nacos的配置中心创建dataid为leadnews-app-gateway的yml配置
spring:
cloud:
gateway:
globalcors:
add-to-simple-url-handler-mapping: true
corsConfigurations:
'[/**]': #匹配所有的请求
allowedHeaders: "*" #允许所有的header
allowedOrigins: "*" #跨域处理 允许所有的域
allowedMethods: #支持的方法
- GET
- POST
- DELETE
- PUT
- OPTION
routes:
# 平台管理
- id: user
uri: lb://leadnews-user
predicates:
- Path=/user/**
filters:
- StripPrefix= 1
环境搭建完成以后,启动项目网关和用户两个服务,使用postman进行测试
请求地址:http://localhost:51601/user/api/v1/login/login_auth
认证过滤器¶
全局过滤器实现jwt校验
思路分析:
- 用户进入网关,网关过滤器进行判断是否是登录
- 如果是登录则路由到后台管理微服务进行登录,用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户。
- 如果不是登录,网关过滤器接收用户携带的TOKEN。
- 网关过滤器解析TOKEN ,判断是否有权限。
- 如果有,则放行,路由到具体微服务。
- 如果没有,则返回401未认证错误。
- 网关过滤器解析TOKEN ,判断是否有权限。
具体实现:
1、在认证过滤器中需要用到jwt的解析,所以需要把工具类拷贝一份到网关微服务
2、在网关微服务中新建全局过滤器:
package com.heima.app.gateway.filter;
import com.heima.app.gateway.util.AppJwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
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//被spring管理的注解
@Slf4j//打印日志的注解
public class AuthorizeFilter implements Ordered, GlobalFilter {//需要实现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)){//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解析后中的用户信息
Object userId = claimsBody.get("id");
//在header中添加新的信息
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
httpHeaders.add("userId", userId + "");
}).build();
//重置header
exchange.mutate().request(serverHttpRequest).build();
} catch (Exception e) {
e.printStackTrace();//打印失败日志
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//6.放行
return chain.filter(exchange);
}
/**
* 优先级设置 值越小 优先级越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
测试:
启动user服务,继续访问其他微服务,会提示需要认证才能访问,这个时候需要在headers中设置设置token才能正常访问。
拦截器¶
- 认证过滤器在网关中实现,网关拿到token之后解析,保存到header中。
- 拦截器在具体的微服务中实现,从header中获取用户id,把用户信息存到当前线程中。请求放到当前线程中。这个请求下,所有的位置都可以从当前线程中获取用户。
①:前端发送上传图片请求,类型为MultipartFile。
②:AuthorizeFilter.java
网关进行token解析后,把解析后的用户信息存储到header
中。
③:自媒体微服务使用拦截器获取到header中的的用户信息,并放入到threadlocal
中。
在heima-leadnews-utils中新增工具类
package com.heima.utils.thread;
import com.heima.model.wemedia.pojos.WmUser;
public class WmThreadLocalUtil {
private final static ThreadLocal<WmUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();
/**
* 添加用户
* @param wmUser
*/
public static void setUser(WmUser wmUser){
WM_USER_THREAD_LOCAL.set(wmUser);
}
/**
* 获取用户
*/
public static WmUser getUser(){
return WM_USER_THREAD_LOCAL.get();
}
/**
* 清理用户
*/
public static void clear(){
WM_USER_THREAD_LOCAL.remove();
}
}
在heima-leadnews-wemedia微服务中新增拦截器
package com.heima.wemedia.interceptor;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.thread.WmThreadLocalUtils;
import lombok.extern.slf4j.Slf4j;
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
public class WmTokenInterceptor implements HandlerInterceptor {
//前置
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//得到header中的信息,并且存入到当前线程中
String userId = request.getHeader("userId");
Optional<String> optional = Optional.ofNullable(userId);
if(optional.isPresent()){
//把用户id存入threadloacl中
WmUser wmUser = new WmUser();
wmUser.setId(Integer.valueOf(userId));
WmThreadLocalUtils.setUser(wmUser);
log.info("wmTokenFilter设置用户信息到threadlocal中...");
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
WmThreadLocalUtil.clear();
}
//后置,清理线程中的数据
/*@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("清理threadlocal...");
WmThreadLocalUtils.clear();
}*/
}
配置使拦截器生效,拦截所有的请求
heima-leadnews/heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/config/WebMvcConfig.java
package com.heima.wemedia.config;
import com.heima.wemedia.interceptor.WmTokenInterceptor;
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 WebMvcConfig implements WebMvcConfigurer {//实现WebMvcConfigurer
@Override
public void addInterceptors(InterceptorRegistry registry) {//添加自定义的拦截器
registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**");
}
}