app开发定制GateWay——向其他服务传递参数数据(思路)

文章目录

前言

app开发定制跳槽去了新公司,app开发定制研究公司的系统架构,app开发定制发现一个很有趣的思路:

GateWay app开发定制解析前端请求携带的token信息,app开发定制并向下游微服务传递。

达到下游微服务不用重复解析token,就能获取当前登录账户的基本信息

其实原理很简单,但记录下实现方式。

GateWay 增加 filter

在gateway服务中,增加filter 过滤器,主要实现获取请求接口中携带的token信息解析token将解析数据继续存放至当前请求对象中

具体实现方式如下所示:

import com.alicp.jetcache.Cache;import com.alicp.jetcache.anno.CreateCache;import lombok.extern.slf4j.Slf4j;import org.springframework.cloud.gateway.filter.GatewayFilterChain;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.core.Ordered;import org.springframework.http.HttpHeaders;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.stereotype.Component;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;import java.net.URI;import java.util.Date;@Slf4j@Componentpublic class UASFilter implements GlobalFilter, Ordered {	@CreateCache(name = "uas:user:login:")	private Cache<String, String> tokenCache;	/**	 * 1.首先网关检查token是否有效,无效直接返回401,不调用签权服务	 * 2.调用签权服务器看是否对该请求有权限,有权限进入下一个filter,没有权限返回401	 *	 * @param exchange	 * @param chain	 * @return	 */	@Override	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {		ServerHttpRequest request = exchange.getRequest();		URI uri = request.getURI();		// 判断是否属于白名单中		if(white(uri.getPath())){			return chain.filter(exchange);		}		log.debug("**********UASFilter start: " + new Date());		try {//			ServerHttpRequest request = exchange.getRequest();			// 获取原始token信息			String authentication = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);			String method = request.getMethodValue();			String url = request.getPath().value();			log.debug("url:{},method:{},headers:{}", url, method, request.getHeaders());			// 根据环境判断是否校验			if (permissionService.ignoreAuthenticationByModule() || isSwaggerUrl(exchange.getRequest().getPath().value())) {				return chain.filter(exchange);			}			//不需要网关签权的url			if (permissionService.ignoreAuthentication(url)) {				return chain.filter(exchange);			}			//登出判断			if (isLogout(authentication)) {				log.error("已经登出或者在其他设备登录,请重新登录!");				return unauthorized(exchange, "已经登出或者在其他设备登录,请重新登录!");			}						// 核心!!!!!			//调用签权服务看用户是否有权限,若有权限进入下一个filter			if (permissionService.hasPermission(authentication, url, method)) {				ServerHttpRequest.Builder builder = request.mutate();				// 原始jwt token				builder.header(GatewayConstans.X_CLIENT_TOKEN, authentication);				//将jwt token中的用户信息传给服务				builder.header(GatewayConstans.X_CLIENT_TOKEN_USER, permissionService.getUserTokenBase64(authentication));				return chain.filter(exchange.mutate().request(builder.build()).build());			}			return unauthorized(exchange);		} finally {			log.debug("**********UASFilter end: " + new Date());		}	}	/**	 * @param token	 * @return boolean	 * @throws	 * @description 登出判断	 * @author yangs	 * Company: 北京九恒星科技股份有限公司	 * @since 2020/8/10 19:20	 */	private boolean isLogout(String token) {		if (StringUtil.isNotEmpty(token) && token.startsWith("Bearer")) {			token = token.replace("Bearer", "").trim();			String loginToken = tokenCache.get(MD5Util.standardMD5(token));			return StringUtil.isBlank(loginToken);		}		return true;	}		/**	 * 网关拒绝,返回401	 *	 * @param msg	 */	protected Mono<Void> unauthorized(ServerWebExchange serverWebExchange, String... msg) {		DataBuffer buffer = serverWebExchange.getResponse()				.bufferFactory().wrap(JSON.toJSONBytes(						CommonResult.error(HttpStatus.UNAUTHORIZED.value(), "未授权!" + (msg.length > 0 ? msg[0] : ""))));		serverWebExchange.getResponse().getHeaders()				.add("Content-Type", "json/plain;charset=UTF-8");		serverWebExchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);		log.error("未授权!", msg);		return serverWebExchange.getResponse().writeWith(Flux.just(buffer));	}	@Override	public int getOrder() {		// 调用优先级, 数字小 优先级高		return 20;	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119

其中最为核心的部分在于:

if (permissionService.hasPermission(authentication, url, method)) {	// 获取当前的请求对象信息	ServerHttpRequest.Builder builder = request.mutate();	// 原始jwt token	builder.header(GatewayConstans.X_CLIENT_TOKEN, authentication);	// 向header中设置新的key,存储解析好的token对应基本信息	builder.header(GatewayConstans.X_CLIENT_TOKEN_USER, permissionService.getUserTokenBase64(authentication));	// exchange.mutate().request(builder.build()).build() 将其继续转化为请求对象	// 向下游传递	return chain.filter(exchange.mutate().request(builder.build()).build());}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这样,只要请求携带了token,并能够成功解析,就会在请求对象的header数据部分,打上x-client-token-user解析后的数据。

其他服务解析

当gateway网关验证完毕后,合法的请求将会继续向内执行,当进入到对应的模块时,此时只需要从请求中获取x-client-token-user对应的登录账户解析数据,并将其保存至ThreadLocal中即可。

一样可以使用filter 过滤器,使用拦截器也可以!

核心代码如下所示:

import org.springframework.web.filter.OncePerRequestFilter;@Order(-1000)@Componentpublic class TokenAuthenticationFilter extends OncePerRequestFilter {		// 重写  doFilterInternal  方法即可	protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {		// 获取请求中header的 x-client-token-user信息        String userToken = httpServletRequest.getHeader("x-client-token-user");        if (userToken != null) {            String json = EncryptUtil.decodeUTF8StringBase64(userToken);            JSONObject jsonObject = JSON.parseObject(json);            HashMap profile = (HashMap)JSON.parseObject(jsonObject.getString("user_name"), HashMap.class);            // 这里是 ThreadLocal 的封装,将获取到的数据存放其中            UserContextHolder.getInstance().setContext(profile);        } else {            UserContextHolder.getInstance().setContext(this.getParamMap());        }        filterChain.doFilter(httpServletRequest, httpServletResponse);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在需要使用的地方,采取下列方式获取即可:

public static Profile getProfile() {    Map<String, String> data = UserContextHolder.getInstance().getContext();    if (null == data) {        throw new RuntimeException("当前请求没有通过网关监控,无法加载登录用户信息!");    } else {        Profile profile = (Profile)BeanUtil.toBean(data, Profile.class);        logger.debug("当前登陆用户信息:{}", profile.toString());        return profile;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

几个工具类

UserContextHolder .java

import java.util.Map;public class UserContextHolder {    private ThreadLocal<Map<String, String>> threadLocal;    private UserContextHolder() {        this.threadLocal = new ThreadLocal();    }    public static UserContextHolder getInstance() {        return UserContextHolder.SingletonHolder.sInstance;    }    public void setContext(Map<String, String> map) {        this.threadLocal.set(map);    }    public Map<String, String> getContext() {        return (Map)this.threadLocal.get();    }    public void clear() {        this.threadLocal.remove();    }    private static class SingletonHolder {        private static final UserContextHolder sInstance = new UserContextHolder();        private SingletonHolder() {        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发