1.gateway使用断言都要干啥
1.1限制应用端时访问,每个端都有标识,否则不予响应
/
/// 1.需要补充内容 ///
/
2.gateway使用过滤器要干啥
2.1 鉴权
2.1.1 非过滤地址忽略
在实际应用场景中,会碰到一些接口,不需要登录,也不需要任何标识就能访问,比如一般新闻相关,还有商品信息等,这时候我们希望网关必要对访问进行额外的处理,我们就需要在网关做一些配置
注意:实际开发中需要登录才可访问的接口远远大于不需要登录的接口,因此一般都是配置不需要登录的接口
创建在resource文件下创建manage.properties
文件
#1.第一种书写方式
manage.excludes[0] = ^/user/v2/api-docs$
manage.excludes[1] = ^/user/hello$
manage.excludes[2] = ^/user/cp$
#1.第二种书写方式,用“,”分隔,方便维护,建议采用第二种
manage.excludes2 = ^/user/v2/api-docs$,\
^/user/hello$,\
^/user/cp$
创建一个ManageExcludeConfig.java
package com.threeking.gateway.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.regex.Pattern;
/**
* 排查参数
* @Author: A.H
* @Date: 2020/10/28 17:22
*/
@Data
@AllArgsConstructor
@Component
@PropertySource(value = {"classpath:conf/manage.properties"})
@ConfigurationProperties(prefix = "manage")
public class ManageExcludeConfig {
//排除列表
private List<String> excludes;
/**
* 验证是否排除
* @return
*/
public boolean isExclude(String uri) {
return urlFilter(excludes, uri);
}
/**
* 验证是否过滤
* @param patternStrs
* @param uri
* @return
*/
private boolean urlFilter(List<String> patternStrs, String uri) {
for (String str : patternStrs) {
if (Pattern.compile(str).matcher(uri).find()) {
return true;
}
}
return false;
}
}
创建一个全局的过滤器
@Slf4j
@Component
public class DomainGlobalFilter implements GlobalFilter, Ordered {
@Autowired
ManageExcludeConfig manageExcludeConfig;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
HttpMethod method = request.getMethod();
String url = request.getURI().getPath();
if(manageExcludeConfig.isExclude(url)){
log.info("非过滤地址,直接跳过.....");
return chain.filter(exchange);
}
// TODO: 其他判断
return null;
}
@Override
public int getOrder() {
return 1;
}
}
当然,也可以用yml文件,或者直接用项目配置文件,这个单独拎出来只是为了方便管理
2.1.2 用户登录认证
使用全局过滤器,通过验证token,或者别的标识,和redis中做对比,完成鉴权,修改上一个过滤器GlobalFilter
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
HttpMethod method = request.getMethod();
String url = request.getURI().getPath();
if(manageExcludeConfig.isExclude(url)){
log.info("非过滤地址,直接跳过.....");
return chain.filter(exchange);
}
// 模拟从前端获取到的用户标识,可以是token,session,或者其他约定的参数
String token = exchange.getRequest().getHeaders().getFirst("token");
if(StringUtils.isEmpty(token)){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
String strInfo = authService.getQueryUserInfo(token);
if(StringUtils.isEmpty(strInfo)){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
创建一个AuthService.java服务类
@Slf4j
@Service
public class AuthService {
/**
* 根据前端标识获取用户信息,可以是从redis,db等中获取
* @param code
* @return
*/
public String getQueryUserInfo(String code){
try {
//根据code从redis或者数据库中获取用户信息
String userId = stringRedisTemplate.opsForValue().get(code);
assert userId != null;
String loginSessionKey = "str"+RED_USER_LOGIN_SESSION + userId;
String session = stringRedisTemplate.opsForValue().get(loginSessionKey);
assert session != null;
UserInfo user = JacksonUtils.toObj(session, UserInfo.class);
log.info(user.toString());
return user.toQuery();
}catch (Exception e){
log.error("获取用户信息时出错:"+e.getMessage());
return null;
}
}
@Data
@NoArgsConstructor
public static class UserInfo{
@JsonProperty(value = "id")
private String userId;
@JsonProperty(value = "nikeName")
private String userName;
/**
* 重写一下toString方案,使其返回&参数拼接的字符串,方便使用
* @return
*/
public String toQuery(){
StringBuilder query = new StringBuilder();
// 注意一定要使用URLEncoder.encode转码,否则传值会有编码问题
for(Field field : this.getClass().getDeclaredFields()){
query.append(field.getName());
query.append('=');
query.append(URLEncoder.encode((String) field.get(this), StandardCharsets.UTF_8));
query.append('&');
}
return query.deleteCharAt(query.length()-1).toString();
}
}
}
2.2 参数传递
用户封装在gateway实现转发,前端不需用传用的的id,code等主键信息,所有的以登录信息为准,接上一个GlobalFilter,修改filter,添加一下代码
// 从身份类里面获取用户信息
query.append(strInfo);
try {
URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).encode(StandardCharsets.UTF_8).build(true).toUri();
ServerHttpRequest newRequest = exchange.getRequest().mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(newRequest).build());
} catch (RuntimeException var9) {
var9.printStackTrace();
throw new IllegalStateException("Invalid URI query: \"" + query.toString() + "\"");
}
在底层服务里面写一个接口
@PostMapping("/ccp")
public String ccp(UserVo userVo)
{
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
assert requestAttributes != null;
HttpServletRequest request = requestAttributes.getRequest();
System.out.println(request.getQueryString());
return userVo.toString();
}
GET方法同样可以获得想要传递的参数