登录界面

ruoyi-ui/src/views/login.vue
点击登录按钮进入handleLogin方法
 
     handleLogin() {
      //验证数据是否合法
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true;
          //如果记住密码被勾选
          if (this.loginForm.rememberMe) {
            //直接在cookie中存入相关信息,过期时间为30天
            Cookies.set("username", this.loginForm.username, { expires: 30 });
            Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });
            Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });
          } else {
            Cookies.remove("username");
            Cookies.remove("password");
            Cookies.remove('rememberMe');
          }
          //调用vuex中的action
          this.$store.dispatch("Login", this.loginForm).then(() => {
            //登录成功后,直接访问主界面
            this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
          }).catch(() => {
            this.loading = false;
            //登录失败,重新生成验证码
            if (this.captchaEnabled) {
              this.getCode();
            }
          });
        }
      });
    }
loginForm
data() {
    return {
      codeUrl: "",
      loginForm: {
        username: "admin",
        password: "admin123",
        rememberMe: false,
        code: "",
        uuid: ""
      },
      loginRules: {
        username: [
          { required: true, trigger: "blur", message: "请输入您的账号" }
        ],
        password: [
          { required: true, trigger: "blur", message: "请输入您的密码" }
        ],
        code: [{ required: true, trigger: "change", message: "请输入验证码" }]
      },
      loading: false,
      // 验证码开关
      captchaEnabled: true,
      // 注册开关
      register: false,
      redirect: undefined
    };
  },
actions中的Login方法
// 登录
    Login({ commit }, userInfo) {
      //将loginForm的值传递过来做赋值
      const username = userInfo.username.trim()
      const password = userInfo.password
      const code = userInfo.code
      const uuid = userInfo.uuid
      return new Promise((resolve, reject) => {
        //调用具体的login方法,对象传到后端
        login(username, password, code, uuid).then(res => {
          //用户登录成功后保存用户的登录状态,
          let data = res.data
          //保存token以及过期时间,调用auth.js中的具体的function给当前cookies存放token以及过期时间的
          //把token存入cookies,调用了auth.js中的setToken
          setToken(data.access_token)
          //触发mutations,把token存入state
          commit('SET_TOKEN', data.access_token)
          setExpiresIn(data.expires_in)
          commit('SET_EXPIRES_IN', data.expires_in)
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
后端登录
 src/main/java/com/ruoyi/auth/controller/TokenController.java
 private final SysLoginService sysLoginService;
    /**
     * 登录方法
     */
    @PostMapping("login")
    public R<Map<String, Object>> login(@Validated @RequestBody LoginBody form) {
        // 用户登录
        String accessToken = sysLoginService.login(form.getUsername(), form.getPassword());
        // 接口返回信息
        Map<String, Object> rspMap = new HashMap<>();
        rspMap.put(Constants.ACCESS_TOKEN, accessToken);
        return R.ok(rspMap);
    }
src/main/java/com/ruoyi/auth/service/SysLoginService.java
    @DubboReference
    private RemoteLogService remoteLogService;
    @DubboReference
    private RemoteUserService remoteUserService;
    @Autowired
    private UserPasswordProperties userPasswordProperties;
    /**
     * 登录
     */
    public String login(String username, String password) {
        //通过用户名得到用户实体类
        LoginUser userInfo = remoteUserService.getUserInfo(username);
        //密码校验,登录次数判断
        //BCrypt.checkpw验证密码是否正确
        checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, userInfo.getPassword()));
        // 获取登录token
        LoginHelper.loginByDevice(userInfo, DeviceType.PC);
        recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
        return StpUtil.getTokenValue();
    }
远程调用src/main/java/com/ruoyi/system/api/RemoteUserService.java
/**
 * 用户服务
 *
 * @author Lion Li
 */
public interface RemoteUserService {
    /**
     * 通过用户名查询用户信息
     *
     * @param username 用户名
     * @return 结果
     */
    LoginUser getUserInfo(String username) throws UserException;
    /**
     * 通过手机号查询用户信息
     *
     * @param phonenumber 手机号
     * @return 结果
     */
    LoginUser getUserInfoByPhonenumber(String phonenumber) throws UserException;
    /**
     * 通过openid查询用户信息
     *
     * @param openid openid
     * @return 结果
     */
    XcxLoginUser getUserInfoByOpenid(String openid) throws UserException;
    /**
     * 注册用户信息
     *
     * @param sysUser 用户信息
     * @return 结果
     */
    Boolean registerUserInfo(SysUser sysUser);
    /**
     * 通过userId查询用户账户
     *
     * @param userId 用户id
     * @return 结果
     */
    String selectUserNameById(Long userId);
}
登录类型src/main/java/com/ruoyi/common/core/enums/LoginType.java
/**
 * 登录类型
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum LoginType {
    /**
     * 密码登录
     */
    PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"),
    /**
     * 短信登录
     */
    SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
    /**
     * 小程序登录
     */
    XCX("", "");
    /**
     * 登录重试超出限制提示
     */
    final String retryLimitExceed;
    /**
     * 登录重试限制计数提示
     */
    final String retryLimitCount;
}
src/main/java/com/ruoyi/system/controller/SysUserController.java#add()
 user.setPassword(BCrypt.hashpw(user.getPassword()));
BCrypt#hashpw()
 public static String hashpw(String password) {
        return hashpw(password, gensalt());//随机创建盐
    }
登录校验src/main/java/com/ruoyi/auth/service/SysLoginService.java#checkLogin()
/**
     * 登录校验
     */
    private void checkLogin(LoginType loginType, String username, Supplier<Boolean> supplier) {
        String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;
        String loginFail = Constants.LOGIN_FAIL;
        Integer maxRetryCount = userPasswordProperties.getMaxRetryCount();
        Integer lockTime = userPasswordProperties.getLockTime();
        // 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip)
        Integer errorNumber = RedisUtils.getCacheObject(errorKey);
        // 锁定时间内登录 则踢出
        if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) {
            recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
            throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
        }
        //密码不正确
        if (supplier.get()) {
            // 是否第一次
            errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1;
            // 达到规定错误次数 则锁定登录
            if (errorNumber.equals(maxRetryCount)) {
                RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
                recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
                throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
            } else {
                // 未达到规定错误次数 则递增
                RedisUtils.setCacheObject(errorKey, errorNumber);
                recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
                throw new UserException(loginType.getRetryLimitCount(), errorNumber);
            }
        }
        // 登录成功 清空错误次数
        RedisUtils.deleteObject(errorKey);
    }
src/main/java/com/ruoyi/common/satoken/utils/LoginHelper.java#loginByDevice()
/**
     * 登录系统 基于 设备类型
     * 针对相同用户体系不同设备
     *
     * @param loginUser 登录用户信息
     */
    public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
        SaStorage storage = SaHolder.getStorage();//当前的请求中
        storage.set(LOGIN_USER_KEY, loginUser);
        storage.set(USER_KEY, loginUser.getUserId());
        SaLoginModel model = new SaLoginModel();
        if (ObjectUtil.isNotNull(deviceType)) {
            model.setDevice(deviceType.getDevice());
        }
        //执行登录操作
        StpUtil.login(loginUser.getLoginId(), model.setExtra(USER_KEY, loginUser.getUserId()));
        //把登录信息存放发哦SaSession中
        //由于SaTokenDao的实现类PlusSaTokenDao里面的方法都使用了Redis,用户信息都被存放到了Redis中
        StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
    }



















