本文使用springcloud的gateway做token校验
登录的本质:拿用户名和密码 换 token。
token会返回给浏览器(存储),当访问的时候,携带token 发起请求。
- token校验图
-
引入redis依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
token校验过滤器
package com.example.filter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
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.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
* token 校验
*/
@Component
public class TokenCheckFilter implements GlobalFilter, Ordered {//GlobalFilter全局过滤器,Ordered 顺序优先级
@Autowired
private StringRedisTemplate redisTemplate; //引入redis依赖,才能使用
/**
* 指定好放行的路径 (可以写在这,也可以写到配置文件中)
* 白名单 (请求路径)
*/
public static final List<String> ALLOW_URL = Arrays.asList("login-service/doLogin","/myUrl");
/**
*前提是? 和前端约定好,一般放在请求头中 key=Authorization, value=bearer token (前缀:bearer+空格+token)
* 1.拿到url (GlobalFilter是全局过滤器, 但是,登录 没有token,要放行)
* 2.判断放行 (登录+白名单 放行,不需要token)
* 然后再校验token
* 3.拿到 请求头
* 4.拿到token
* 5.校验 (看redis 中 有没有,如果没有就拦截,有就放行)
* 6.放行/拦截
*
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//拿到url 请求路径
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (ALLOW_URL.contains(path)){
//放行
return chain.filter(exchange);
}
//校验
HttpHeaders headers = request.getHeaders(); //拿到请求头
List<String> authorization = headers.get("Authorization");
if (!CollectionUtils.isEmpty(authorization)){ //key不为空,取第一个
String token = authorization.get(0);
if (StringUtils.hasText(token)){ //token不空
//约定好有前缀 bearer token
String realToken = token.replaceFirst("bearer ", "");// replaceFirst替换字符串前面
if (StringUtils.hasText(realToken) && redisTemplate.hasKey(realToken)){
//如果 请求头中 有token, 并且 redis中 有这个token(不带前缀)
//放行
return chain.filter(exchange);
}
}
}
//拦截
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().set("content-type","application/json;charset=utf-8");
HashMap<String, Object> map = new HashMap<>(4);
//返回401
map.put("code", HttpStatus.UNAUTHORIZED.value());
map.put("msg","未授权");
ObjectMapper objectMapper = new ObjectMapper();
byte[] bytes = new byte[0]; //以字节形式 写到objectmapper
try {
bytes = objectMapper.writeValueAsBytes(map);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
DataBuffer wrap = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(wrap));
}
@Override
public int getOrder() {
return 2; //先校验ip,再校验token
}
}
-
流程:
前提: 和前端约定好
token一般放在请求头中 key=Authorization, value=bearer token
(前缀:bearer+空格+token)- 1.拿到url (GlobalFilter是全局过滤器, 但是,登录 没有token,要放行)
- 2.判断放行 (登录+白名单 放行,不需要token)
- 然后再校验token
- 3.拿到 请求头
- 4.拿到token
- 5.校验 (看redis 中 有没有,如果没有就拦截,有就放行)
- 6.放行/拦截