前端

main.js中导入了import '@/permission'

permission.js中设置了路由全局守卫,见下图代码所示:

Jeecg登录流程 - 图1

上述代码中Vue.ls.get(ACCESS_TOKEN)是判断浏览器localStorage中是否有ACCESS_TOKEN如果有,则表明已经登录了

否则需要路由到next({ path: '/user/login', query: { redirect: to.fullPath } })

假如我们还未登录,查看路由表(在router.config.js中),如下图所示:

Jeecg登录流程 - 图2

发现/user/login可知对应的登录界面是@/views/user/Login.vue

顺势在@/views/user/Login.vue中找到登录按钮

Jeecg登录流程 - 图3

绑定的方法是handleSubmit

Jeecg登录流程 - 图4

handleSubmit中真正触发登录请求的地方是上图中红色框中的代码。

that.Login实际是this.LoginLogin方法是通过vuex组件分发action技术引入的

Jeecg登录流程 - 图5

关于项目中如何使用vuex请配合vuex官方文档仔细阅读下图所示部分代码

Jeecg登录流程 - 图6

我们刚刚提到的Login方法真正定义的地方是在user.js中,代码位置如下图所示:

Jeecg登录流程 - 图7

上图标记1实际上是发起了一个axios请求(即请求后端登录接口/sys/login),代码如下:

  1. export function login(parameter) {
  2. return axios({
  3. url: '/sys/login',
  4. method: 'post',
  5. data: parameter
  6. })
  7. }

标记2是后端登录成功后前端设置一些localStorage、并且更新vuex state,比较关键的是Vue.ls.set(ACCESS_TOKEN, result.token, 7 * 24 * 60 * 60 * 1000)

后端

后端对应的登录接口位置如图:

Jeecg登录流程 - 图8

分段解析下:

String username = sysLoginModel.getUsername();
String password = sysLoginModel.getPassword();
//update-begin--Author:scott  Date:20190805 for:暂时注释掉密码加密逻辑,有点问题
//前端密码加密,后端进行密码解密
//password = AesEncryptUtil.desEncrypt(sysLoginModel.getPassword().replaceAll("%2B", "\\+")).trim();//密码解密
//update-begin--Author:scott  Date:20190805 for:暂时注释掉密码加密逻辑,有点问题

//update-begin-author:taoyan date:20190828 for:校验验证码
String captcha = sysLoginModel.getCaptcha();
if(captcha==null){
  result.error500("验证码无效");
  return result;
}
String lowerCaseCaptcha = captcha.toLowerCase();
String realKey = MD5Util.MD5Encode(lowerCaseCaptcha+sysLoginModel.getCheckKey(), "utf-8");
Object checkCode = redisUtil.get(realKey);
if(checkCode==null || !checkCode.equals(lowerCaseCaptcha)) {
  result.error500("验证码错误");
  return result;
}

这一段主要是拿前端传过来的验证码和redis中的验证码进行比较

//1. 校验用户是否有效
SysUser sysUser = sysUserService.getUserByName(username);
result = sysUserService.checkUserIsEffective(sysUser);
if(!result.isSuccess()) {
  return result;
}

//2. 校验用户名或密码是否正确
String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
String syspassword = sysUser.getPassword();
if (!syspassword.equals(userpassword)) {
  result.error500("用户名或密码错误");
  return result;
}

检查用户名是否有效以及密码是否正确

//将用户信息放进result中
userInfo(sysUser, result);
sysBaseAPI.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null);

return result;

主要就是userInfo(sysUser, result);,作用是将用户信息放进result中。

我们不用看userInfo(sysUser, result)的内部实现,直接看result的数据结构(数据样例)如何:

Jeecg登录流程 - 图9

其他

登录完成之后,后续对服务器的请求都会从Vue.ls.get(ACCESS_TOKEN)取出ACCESS_TOKEN,放入到http头部X-Access-Token中。

例如 请求个人中心页面http://localhost:3000/account/center

Jeecg登录流程 - 图10

Jeecg登录流程 - 图11

注意上图请求头中有X-Access-Token

前端是怎么把X-Access-Token放进去的呢?我们来跟踪代码:

首先找到个人中心这个页面center/Index.vue

Jeecg登录流程 - 图12

发现this.$http并没有设置头部X-Access-Token,那肯定在一个地方全局配置的。

经过一番搜索,发现是在utils/request.js

Jeecg登录流程 - 图13

在这里拦截每一个请求,给它们设置X-Access-Token头部


后端服务器配置了shiro过滤器,会拦截一些特定url,比如上面的/workplace/teams

Jeecg登录流程 - 图14

Jeecg登录流程 - 图15

Jeecg登录流程 - 图16

后端会从被拦截到的请求头中取出X_ACCESS_TOKEN,然后交给shiro realm 认证这个token.

首先token不能为空,然后在checkUserTokenIsEffect(token)中校验用户名、密码以及是否过期

注意:后端token有效时间设置为EXPIRETIME = 30 60 _ 1000; 30分钟 和 前端7天是不一样的