Skip to main content
Version: 1.x

代理配置

前端调用后端 API 接口都统一由 BFF 来转发,假设后端有一个服务:sample, 地址是:http://localhost:9999

前端想要调用这个 web 服务,下面给出操作步骤

一、 配置服务代理

打开菜单 【服务代理配置】,填写如下信息

  1. 代理路径,每一个环境这个值必须唯一,这里填写 /sample,注意前面有一个/不要漏了
  2. 代理环境,通过这个参数可以区分不同的开发环境,当和后端进行本地联调时,这个值非常有用,这个默认值是default,代表的是真正部署在线上的正式环境,每一个服务有且只有一个默认的代理路径,在这里先填写default
  3. 代理地址,就是后端的服务地址,在这里填写http://localhost:9999,这里后面的/sample是可选的,这个看后端提供的服务地址而定
  4. 服务名称,每一个服务都得有一个名词,这个要起个有意义的名称,方便后续定位问题,这里填写示例服务

JPG

二、 后端如何获取当前用户信息

通过 BFF 转发的所有请求,都会携带用户信息,目前可通过如下两种方式获取用户信息

第一种,获取 header 中的 authorization 信息,去掉前面的Bearer,剩下的就是 jwt 用户信息了,如果是 java,可通过 com.auth0 这个 jar 包中的 new JWTVerifier("quantex")解析,就能拿到用户信息了

第二种,直接获取 header 中 user-info 信息,然后用 decodeURI 解码,也可以拿到用户信息,格式如下:

{
"code": "0001", // 用户Id(一事通ID)
"name": "xxx" // 用户名称
// ... 其它
}

如果获取不到用户信息,那么用户属于未登录,需要抛出异常,返回如下信息(建议)

{
"code": 401, // 401代表认证失败
"msg": "认证失败" //
}

下面为模拟后端获取用户信息的代码

1. 通过 authorization 获取用户信息(Node.js 版本)

const getUserInfoByToken = ctx => {
// 第一步,获取request中authorization请求头
const authorization = ctx.headers.authorization;
if (!authorization) {
// 如果没有认证信息,则说明请求非法,返回401认证失败
ctx.body = {
code: 401,
msg: '用户认证失败'
};
return;
}

// 第二步,提取有效的jwt token信息
const token = authorization.split(' ')[1];

// 第三步,校验并解析token,获取到用户信息
let userInfo = null;
try {
userInfo = jwt.verify(token, 'quantex');
} catch (e) {
ctx.body = {
code: 401,
msg: '非法的用户认证信息'
};
return;
}

// 第四步(可选),拿到用户信息后,可以进一步校验token的有效期
if (userInfo.exp < new Date().getTime()) {
ctx.body = {
code: 401,
msg: '认证信息失效'
};
return;
}
// 最后,就可以很愉快的处理业务逻辑啦,以上的代码建议放在公共的地方(拦截器)
console.log(userInfo);
return userInfo;
};

2. 通过 user-info 获取用户信息(Node.js 版本)

const getUserInfo = ctx => {
// 第一步,获取request中user-info请求头
const userInfoEncode = ctx.headers['user-info'];
if (!userInfoEncode) {
// 如果没有用户信息,则说明请求非法,返回401认证失败
ctx.body = {
code: 401,
msg: '用户认证失败'
};
return;
}

// 第二步,用decodeURI解码
const userInfoJson = decodeURI(userInfoEncode);

// 第三步,转化成用户信息对象
let userInfo = null;
try {
userInfo = JSON.parse(userInfoJson);
} catch (e) {
ctx.body = {
code: 401,
msg: '非法的用户认证信息'
};
return;
}
// 最后,就可以很愉快的处理业务逻辑啦,以上的代码建议放在公共的地方(拦截器)
console.log(userInfo);
return userInfoJson;
};

3. 通过 authorization 获取用户信息(Java 版本)

  1. pom.xml 导入 jwt 的包
 <!-- jwt -->
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>2.2.0</version>
</dependency>
  1. 编写 jwt 的工具类,仅提供解密功能就好
import com.auth0.jwt.JWTSigner;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.internal.com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.Map;

public class JWT {
private static final String SECRET = "quantex";

private static final String EXP = "exp";

private static final String PAYLOAD = "payload";

//解密,传入一个加密后的token字符串和解密后的类型
public static<T> T unsign(String jwt, Class<T> classT) {
final JWTVerifier verifier = new JWTVerifier(SECRET);
try {
final Map<String,Object> claims= verifier.verify(jwt);
if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
long exp = (Long)claims.get(EXP);
long currentTimeMillis = System.currentTimeMillis();
if (exp > currentTimeMillis) {
String json = (String)claims.get(PAYLOAD);
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, classT);
}
}
return null;
} catch (Exception e) {
return null;
}
}

}

三、 本地开发时,前后端如何联调

上面配置的服务代理是正式环境,本地的话可以再增加一个环境

第一步,打开【服务代理配置】页面,再增加一个 test环境

  1. 代理路径,跟正式环境一样
  2. 代理环境,填写test,这个可以随便起个名字,只有能对应起来就行
  3. 代理地址, 修改为本地的地址,例如 http://localhost:9998
  4. 服务名称,跟正式环境一样

第二步,修改前端代码 config/config.js 配置文件中的 serviceProxyEnv 配置,将sample服务指向test

PNG

然后重新 npm start,请求就会转发到本地的后端服务,环境切换大概示意图如下

PNG

四、 常见问题

1. 后端报跨域问题

在 Spring boot 中通常是可以通过增加过滤器,对返回响应中的请求头增加相关配置,具体代码如下:

@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;

HttpServletRequest reqs = (HttpServletRequest) req;

response.setHeader("Access-Control-Allow-Origin",reqs.getHeader("Origin"));
// response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}

@Override
public void init(FilterConfig filterConfig) {}

@Override
public void destroy() {}

}