整理思路
- 由网关Zuul进行Token颁布
- 由网关Zuul进行
登录认证
拦截 - 拦截的配置等可以用Mysql存储配置或者配置文件,只配置默认放开的路径即可
- 拦截后把在zuul中的token,换成跟后台用户关联的Uid等
整体分为2步
- 创建Pre类型ZuulFilter转换token,加入Request Header中
- 下游业务服务接收转换的Uid
Pre Zuul Filter
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
- 过滤器类型 : FilterConstants.PRE_TYPE
- 过滤器优先级:跟debug 一样 FilterConstants.DEBUG_FILTER_ORDER
- 是否开启:可以获取path判断某些不过滤
- 可以自定义增加请求参数或者请求头
- 可以自定义不传递,直接返回的响应码和响应体
- 可以根据
service-id
判断是否处理 - 自定义header如果起始request中已有,会被zuul覆盖
@Component
public class PreTokenFilter extends ZuulFilter {
@Autowired
private RouteLocator routeLocator;
private UrlPathHelper urlPathHelper = new UrlPathHelper();
@Override
public String filterType() { // 过滤器类型
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() { //过滤器优先级
return FilterConstants.DEBUG_FILTER_ORDER;
}
@Override
public boolean shouldFilter() { // 过滤器是否启用
RequestContext ctx = RequestContext.getCurrentContext();
final String requestURI = this.urlPathHelper
.getPathWithinApplication(ctx.getRequest());
Route route = this.routeLocator.getMatchingRoute(requestURI);
if (route != null) {
// roadnet-service
String location = route.getLocation();
if (StrUtil.equals(location, "roadnet-service")) {
return true;
}
}
return false;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest(); // 获取请求 HttpServletRequest
String token = request.getHeader("token");
if (StrUtil.isBlank(token)) {
token = request.getParameter("token");
}
if (null == token || "".equals(token)) {
ctx.setSendZuulResponse(false); // 直接返回,不向后传递了
ctx.getResponse().setContentType("application/json; charset=utf-8");
ctx.setResponseStatusCode(401); // 返回 http code 401
Dict dict = Dict.create().set("code", "101").set("msg", "兄弟裂开了,token is not found");
ctx.setResponseBody(JSONUtil.toJsonPrettyStr(dict));
}
setExtRequestHeader(ctx);
setExtRequestQueryParams(ctx);
return null;
}
private void setExtRequestHeader(RequestContext ctx) {
ctx.addZuulRequestHeader("deptId", "lakerDept");
ctx.addZuulRequestHeader("userId", "laker");
}
private void setExtRequestQueryParams(RequestContext ctx) {
//将转换后的数据放入请求参数中
Map<String, List<String>> requestQueryParams = ctx.getRequestQueryParams();
if (requestQueryParams == null) {
requestQueryParams = new HashMap<>();
}
//将要新增的参数添加进去,被调用的微服务可以直接 去取,就想普通的一样,框架会直接注入进去
ArrayList<String> paramsList = new ArrayList<>();
paramsList.add("lakerDept");
ArrayList<String> paramsList1 = new ArrayList<>();
paramsList1.add("laker");
requestQueryParams.put("deptId", paramsList);
requestQueryParams.put("userId", paramsList1);
ctx.setRequestQueryParams(requestQueryParams);
}
}
下游业务服务接收
@GetMapping("/info")
public String info() {
System.err.println("用户ID:" + request.getHeader("userId"));
}
其内部可以使用Filter或者拦截器去验证所有请求以及存放标记当前会话的userid
public class HttpHeaderParamFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json; charset=utf-8");
String uid = httpRequest.getHeader("uid");
if(StrUtils.isNotBlank(uid)){
RibbonFilterContextHolder.getCurrentContext().add("uid", uid);
chain.doFilter(httpRequest, response);
} else {
throw new BusinessException("没登录玩个锤子");
}
}
@Override
public void destroy() {
}
}