在日常开发中,经常我们需要在请求controller时传递用户信息。那么有没有一种方式可以让我们的程序在用户登录后自动向controller接口中注入用户用户信息呢?
答案是yes!!!现在我们来看看博主当前的项目是如何实现的吧!
1.首先登录时,账号密码校验通过后生成一个token,并将用户基本信息存入redis。将key返回给前端。
2.前端收到后将当前用户的token缓存起来,每次像后台发请求时在header中携带token。
3.重点来了,在gateway中添加一个过滤器,在过滤器中获取到tocken,用token作为键,去redis中获取用户的账号,以及诊所等基本信息。并将这些信息拼接到url后面。代码示例如下:
package com.lvyuanji.gateway.filter;
import cn.hutool.core.util.StrUtil;
import com.lvyuanji.account.api.service.IAccountAdminApiService;
import com.lvyuanji.common.consts.SystemConsts;
import com.lvyuanji.common.vo.redis.AccountEntity;
import com.lvyuanji.common.vo.redis.SalesmanLoginVo;
import com.lvyuanji.stock.api.service.ISalesmanApiService;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
@Component
public class AccountRequestParameterGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
private static final Logger logger = LoggerFactory.getLogger(AccountRequestParameterGatewayFilterFactory.class);
@Reference
private IAccountAdminApiService accountAdminApiService;
@Reference
private ISalesmanApiService salesmanApiService;
@Override
public GatewayFilter apply(NameValueConfig config) {
return new GatewayFilter() {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
URI uri = request.getURI();
StringBuilder query = new StringBuilder();
String originalQuery = uri.getRawQuery();
if (StringUtils.hasText(originalQuery)) {
query.append(originalQuery);
if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
query.append('&');
}
}
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
// TODO urlencode?
query.append(config.getName());
query.append('=');
query.append(value);
String method = request.getMethodValue();
if ("GET".equals(method.toUpperCase())) {
logger.info("【GET】请求 URI:{} 参数:{}", uri.getPath(), query);
}
//获取redis中用户的缓存信息,拼接到请求参数后面
HttpHeaders headers = exchange.getRequest().getHeaders();
String token = headers.getFirst("token");
if (StringUtils.hasText(token)) {
AccountEntity accountEntity = accountAdminApiService.loginAccountAdmin(token);
if (accountEntity != null) {
//先去header中获取诊所ID,如果没有去请求参数中获取
String tenantId = headers.getFirst("tenantId");
if (StringUtils.hasText(tenantId)) {
accountEntity.setTenantId(Long.valueOf(tenantId));
} else {
List tenantIds = request.getQueryParams().get("tenantId");
if (!CollectionUtils.isEmpty(tenantIds)) {
accountEntity.setTenantId(Long.valueOf(tenantIds.get(0)));
}
}
//封装其他参数
Map beanMap = beanValue(accountEntity);
if (!CollectionUtils.isEmpty(beanMap)) {
for (String key : beanMap.keySet()) {
try {
Object obj = beanMap.get(key);
String param = obj != null ? URLEncoder.encode(obj.toString(), "UTF-8") : null;
query.append('&').append(key).append('=').append(param);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
} else {
//获取应用类型
String appType = StrUtil.isNotEmpty(request.getHeaders().getFirst(SystemConsts.APP_TYPE)) ? request.getHeaders().getFirst(SystemConsts.APP_TYPE) : "";
if ("applets".equals(appType)) {
query.append("&").append("userType").append("=").append("SALESMAN");
}
}
} else {
String tenantId = headers.getFirst("tenantId");
if (StringUtils.hasText(tenantId)) {
query.append('&').append("tenantId").append('=').append(tenantId);
}
}
try {
String queryStr = filterOperator(query.toString());
URI newUri = UriComponentsBuilder.fromUri(uri)
.replaceQuery(queryStr).build(true).toUri();
ServerHttpRequest req = exchange.getRequest().mutate().uri(newUri)
.build();
return chain.filter(exchange.mutate().request(req).build());
} catch (RuntimeException ex) {
throw new IllegalStateException(
"Invalid URI query: \"" + query.toString() + "\"");
}
}
@Override
public String toString() {
return filterToStringCreator(AccountRequestParameterGatewayFilterFactory.this)
.append(config.getName(), config.getValue()).toString();
}
};
}
/**
* 过滤特殊字符
*
* @param target
* @return
*/
private String filterOperator(String target) {
target = target.replace("[", "");
return target.replace("]", "");
}
/**
* 获取bean的属性和值
*
* @param bean
* @return
*/
private Map beanValue(AccountEntity bean) {
Map result = new HashMap<>();
Class clazz = bean.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String name = field.getName();
field.setAccessible(true);
Object value = null;
try {
value = field.get(bean);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (value != null) {
result.put(name, value);
}
}
return result;
}
}
4.spring参数解析器会自动将url后面的参数解析为对应的AccountBean,这样我们在需要用到Account信息的Controller方法中只需要在参数中加入AccountBean参数即可,。前端也不必刻意传Account信息,因为Header中携带了Token。