前言
记录前后端分离的系统应用下应用场景————用户信息传递
需求缘起
照例先看看web
系统的一张经典架构图,这张图参考自网络:
在 Dubbo 自定义异常,你是怎么处理的? 中已经对该架构做了简单说明,这里不再描述。
简单描述下在该架构中用户信息(如userId)的传递方式
:
现在绝大多数的项目都是前后端分离的开发模式,采用token
方式进行用户鉴权:
- 客户端(pc,移动端,平板等)首次登录,服务端签发
token
,在token
中放入用户信息(如userId)
等返回给客户端 - 客户端访问服务端接口,需要在头部携带
token
,跟表单一并提交到服务端 - 服务端在
web
层统一解析token
鉴权,同时取出用户信息(如userId)
并继续向底层传递,传到服务层操作业务逻辑 - 服务端在
service
层取到用户信息(如userId)
后,执行相应的业务逻辑操作
问题:
为什么一定要把用户信息(如userId)
藏在token
中,服务端再解析token
取出?直接登录后向客户端返回用户信息(如userId)
不是更方便么?
跟用户强相关的信息是相当敏感的,一般用户信息(如userId)
不会直接明文暴露给客户端,会带来风险。
单体应用下用户信息(如userId)
的传递流程
什么是单体应用? 简要描述就是web
层,service
层全部在一个jvm
进程中,更通俗的讲就是只有一个项目
。
登录签发 token
看看下面的登录接口伪代码:
web
层接口:
@Loggable(descp = "用户登录", include = "loginParam")
@PostMapping("/login")
public BaseResult<LoginVo> accountLogin(LoginParam loginParam) {
return mAccountService.login(loginParam);
}
复制代码
service
层接口伪代码:
public BaseResult<LoginVo> login(LoginParam param) throws BaseException {
//1.登录逻辑判断
LoginVo loginVo = handleLogin(param);
//2.签发token
String subject = userId;
String jwt = JsonWebTokenUtil.issueJWT(UUID.randomUUID().toString(), subject,
"token-server", BaseConstants.TOKEN_PERIOD_TIME, "", null, SignatureAlgorithm.HS512);
loginVo.setJwt(jwt);
return ResultUtil.success(loginVo);
}
复制代码
注意到上述伪代码中,签发token
时把userId
放入客户标识subject
中,签发到token
中返回给客户端。这里使用的是JJWT
生成的token
引入依赖:
<!--jjwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.9</version>
</dependency>
复制代码
相关工具类JsonWebTokenUtil
:
public class JsonWebTokenUtil {
//秘钥
public static final String SECRET_KEY = BaseConstant.SECRET_KEY;
private static final ObjectMapper MAPPER = new ObjectMapper();
private static CompressionCodecResolver codecResolver = new DefaultCompressionCodecResolver();
//私有化构造
private JsonWebTokenUtil() {
}
/* *
* @Description json web token 签发
* @param id 令牌ID
* @param subject 用户标识
* @param issuer 签发人
* @param period 有效时间(秒)
* @param roles 访问主张-角色
* @param permissions 访问主张-权限
* @param algorithm 加密算法
* @Return java.lang.String
*/
public static String issueJWT(String id,String subject, String issuer, Long period,
String roles, String permissions, SignatureAlgorithm algorithm) {
// 当前时间戳
Long currentTimeMillis = System.currentTimeMillis();
// 秘钥
byte[] secreKeyBytes = DatatypeConverter.parseBase64Binary(SECRET_KEY);
JwtBuilder jwtBuilder = Jwts.builder();
if (StringUtils.isNotBlank(id)) {
jwtBuilder.setId(id);
}
if (StringUtils.isNotBlank(subject)) {
jwtBuilder.setSubject(subject);
}
if (StringUtils.isNo