农产品后台管理系统(二)——基于Redis实现JWT单点登录

  我们这里使用Redis来实现JWT单点登录,不熟悉Redis的朋友也可以借此来了解一下Redis的基本使用。

  在此之前,先解释一下什么是单点登录。最普通的那种基于Session+Cookie的实现方式,可以实现简单的登录,,它的使用场景就是单机系统的情况下。假如我们的系统是分布式系统,那么不同的系统下就得保存一份同样的登录状态数据,虽然也可以实现,但这无疑是浪费了很多空间。
  基于Redis的单点登录,我们只需要在Redis中保存一份数据即可,不管是什么样系统,获取登录状态的时候,我们只需要去Redis中获取即可。


这里介绍后端的实现代码

Controller层

 /**
     * 用户登录
     *
     * @param userLoginRequest
     * @param request
     * @return
     */
    @ExcludePath
    @PostMapping("/login")
    public BaseResponse<UserVO> userLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request) {
        UserVO userVO = userService.userLogin(userLoginRequest, request);
        return ResultUtils.success(userVO, "登录成功");
    }

Controller上面的@ExcludePath注解,是自定义的一个拦截器放行的注解,只需要在拦截器的定义中,判断方法上时候有这个注解,有的话就放行,这省去了我们在配置注册器中添加excludePath,我觉得这样更简便一些。

Service层


@Override
@Transactional
 public UserVO userLogin(UserLoginRequest userLoginRequest, HttpServletRequest request) {
     if (userLoginRequest == null) {
         throw new BusinessException(ErrorCode.PARAMS_ERROR, "缺少登录参数");
     }
     String userAccount = userLoginRequest.getUserAccount();
     String userPassword = userLoginRequest.getUserPassword();
     // 1. 校验
     if (StringUtils.isAnyBlank(userAccount, userPassword)) {
         throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
     }
     if (userAccount.length() < 4) {
         throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误");
     }
     if (userPassword.length() < 8) {
         throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
     }
     // 2. 加密
     String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
     // 查询用户是否存在
     QueryWrapper<User> queryWrapper = new QueryWrapper<>();
     queryWrapper.eq("user_account", userAccount);
     queryWrapper.eq("user_password", encryptPassword);
     User user = userMapper.selectOne(queryWrapper);
     // 用户不存在
     if (user == null) {
         throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
     }
     // 3. 记录用户的登录态
     Gson gson = new Gson();
     String userJson = gson.toJson(user);
     // 4. 生产随机Token,作为登录凭证
     String token = UUID.randomUUID().toString();
     stringRedisTemplate.opsForValue().set(USER_TOKEN + token, userJson);
     // 5. 为Token设置过期时间
     stringRedisTemplate.expire(USER_TOKEN + token, 30L, TimeUnit.MINUTES);
     // 6. 返回User视图数据(安全的数据)
     UserVO userVO = new UserVO();
     BeanUtils.copyProperties(user, userVO);
     userVO.setToken(token);
     // 7. 设置登录状态
     userVO.setLogin(true);
     return userVO;
 }

真正的业务逻辑是在Service层中。

  不管做什么,我们都得对传递进来的参数进行校验,防止出现空指针异常,或者出现格式不正确的情况。即使前端有校验,我们后端也得做校验,更加的严谨。
如果我们校验不通过就直接抛出一个异常,这里的异常也是自定义的,我们这里对返回结果都统一了格式。

自定义异常类

/**
 * 自定义异常类
 *
 * @author jl
 */
public class BusinessException extends RuntimeException {

    private final int code;

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
    }

    public BusinessException(ErrorCode errorCode, String message) {
        super(message);
        this.code = errorCode.getCode();
    }

    public int getCode() {
        return code;
    }
}

自定义错误枚举类

/**
 * 错误码
 *
 * @author jl
 */
public enum ErrorCode {

    SUCCESS(0, "ok"),
    PARAMS_ERROR(40000, "请求参数错误"),
    NOT_LOGIN_ERROR(40100, "未登录"),
    NO_AUTH_ERROR(40101, "无权限"),
    NOT_FOUND_ERROR(40400, "请求数据不存在"),
    FORBIDDEN_ERROR(40300, "禁止访问"),
    SYSTEM_ERROR(50000, "系统内部异常"),
    OPERATION_ERROR(50001, "操作失败");

    /**
     * 状态码
     */
    private final int code;

    /**
     * 信息
     */
    private final String message;

    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

}

在抛出异常的时候同时写了相关的message,这个参数前端可以直接显示出来,如下图:
在这里插入图片描述
参数校验完参数之后,下边我们说一下登录的思路:
  第一步就是去数据库中查找,登录成功之后才是我们这里要说的重点,如何将这个登录状态保存。
首先随机生成一个UUID,作为登录凭证(可以类比于Seesion识别时候要用的JSESSIONID)。因为用的Redis,所以我们这里为了实现业务之间的区分,给这个Token加上一个业务前缀,过期时间设置为30分钟。
最后将这个用户的信息和这个Token登录信息一同返回,但要注意的是,返回用户信息不能将所有信息都返回(例如:密码),为了信息的安全,所以,这里定义了一个UserVO用户视图对象,通过BeanUtils.copyProperties()可以实现属性的拷贝(很好用)。
最后返回这个UserVo即可。


以上就是Redis实现单点登录的基本思路,后面讲前端如何保存这个登录状态,以及Redis登录续期,登陆状态校验等…
如果文章中有描述不准确或者错误的地方,还望指正。您可以留言📫或者私信我。🙏
最后希望大家多多 关注+点赞+收藏^_^,你们的鼓励是我不断前进的动力!!!
感谢感谢~~~🙏🙏🙏

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艺术留白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值