【wiki知识库】08.添加用户登录功能--后端SpringBoot部分

news2024/12/28 22:45:24

目录

一、今日目标 

二、SpringBoot后端实现

2.1 新增UserLoginParam

2.2 修改UserController

2.3 UserServiceImpl代码

2.4 创建用户上下文工具类

2.5 通过token校验用户(重要)

2.6 创建WebMvcConfig

2.7 用户权限校验拦截器


一、今日目标 

上篇文章链接:【wiki知识库】08.添加用户登录功能--前端Vue部分修改-CSDN博客

这篇文章主要是实现一下用户登录功能的后端部分,登录功能需要使用redis,不懂redis可以看我之前的一篇文章。

Redis文章链接:【Spring】SpringBoot整合Redis,用Redis实现限流(附Redis解压包)_springboot 限流 redis-CSDN博客

那么为什么要用到Redis呢?

这个问题关系到整个系统的用户校验,当我们登录成功的时候,后端会生成一个用于用户校验的token值,然后把这个值传给前端,每次用户请求后端的时候都要带上这个token值,这个token的值当中记录了当前登录的用户是谁,还有过期时间等信息,这样子就可以防止那些没有登陆的用户去直接访问我们的后端调用接口。所以这个token还是需要妥善保管的,一旦token丢失别人就可能用你的token去发送请求,修改你的数据。

二、SpringBoot后端实现

2.1 新增UserLoginParam

这里也做了校验,其实这个事情完全可以放到前端实现,但是也要考虑到有直接调用接口的情况,这时也要给出错误提示。

@Data
public class UserLoginParam {

    @NotEmpty(message = "【用户名】不能为空")
    private String loginName;

    @NotEmpty(message = "【密码】不能为空")
    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,32}$", message = "【密码】规则不正确")
    private String password;

}

2.2 修改UserController

直接上代码吧。这里拿到了用户的账号和用户的密码,然后判断加密后的密码和数据库中取出来的用户密码是否相同,如果相同那么就可以登陆。登陆后通过工具类生成一个不会重复的Long类型的值作为该用户的token,然后以token为key,登录用户创建的对象作为值,保存到redis当中,以便于后续用户访问接口时,通过用户token来判断是哪个用户访问接口。

  @PostMapping("/login")
  public CommonResp login(@Valid @RequestBody UserLoginParam req) {
    req.setPassword(DigestUtils.md5DigestAsHex(req.getPassword().getBytes()));
    System.out.println(req);
    UserLoginVo userLoginResp = userService.login(req);
    Long token = snowFlake.nextId();
    userLoginResp.setToken(token.toString());
    redisTemplate.opsForValue().set(token.toString(), JSONObject.toJSONString(userLoginResp), 3600 * 24, TimeUnit.SECONDS);
    return new CommonResp(true,"登录成功",userLoginResp);
  }

  @GetMapping("/logout/{token}")
  public CommonResp logout(@PathVariable String token) {
    boolean res = redisTemplate.delete(token);
    String message = Boolean.TRUE.equals(res) ? "登出成功":"登出失败";
    return new CommonResp(true,message,null);
  }

2.3 UserServiceImpl代码

这个代码没有什么好说的,就是查找一次数据库进行账号密码的匹配。

public UserLoginVo login(UserLoginParam req) {
        User userDb = selectByLoginName(req.getLoginName());
        if (ObjectUtils.isEmpty(userDb)) {
            // 用户名不存在
            throw new RuntimeException("用户名不存在");
        } else {
            if (userDb.getPassword().equals(req.getPassword())) {
                // 登录成功
                UserLoginVo userLoginResp = CopyUtil.copy(userDb, UserLoginVo.class);
                return userLoginResp;
            } else {
                // 密码不对
                throw new RuntimeException("密码错误");
            }
        }
    }

2.4 创建用户上下文工具类

这个工具类用户用户登录后保存当前用户的上下文。

public class LoginUserContext implements Serializable {

    private static ThreadLocal<UserLoginVo> user = new ThreadLocal<>();

    public static UserLoginVo getUser() {
        return user.get();
    }

    public static void setUser(UserLoginVo user) {
        LoginUserContext.user.set(user);
    }

}

2.5 通过token校验用户(重要)

校验用户token需要使用到拦截器或者过滤器,这里我使用拦截器进行用户token的校验。整体的校验流程如下

 以下就是登录拦截器的代码,

/**
 * 拦截器:Spring框架特有的,常用于登录校验,权限校验,请求日志打印
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {
    private static final Logger LOG = LoggerFactory.getLogger(LoginInterceptor.class);
    @Resource
    private RedisTemplate redisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // OPTIONS请求不做校验,
        // 前后端分离的架构, 前端会发一个OPTIONS请求先做预检, 对预检请求不做校验
        if (request.getMethod().toUpperCase().equals("OPTIONS")) {
            return true;
        }
        String path = request.getRequestURL().toString();
        LOG.info("接口登录拦截:,path:{}", path);

        //获取header的token参数
        String token = request.getHeader("token");
        LOG.info("登录校验开始,token:{}", token);
        if (token == null || token.isEmpty()) {
            LOG.info("token为空,请求被拦截");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        Object object = redisTemplate.opsForValue().get(token);
        // 证明redis中的用户信息过期了,需要重新登陆
        if (object == null) {
            LOG.warn("token无效,请求被拦截");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        } else {
            LOG.info("已登录:{}", object);
            LoginUserContext.setUser(JSON.parseObject((String) object, UserLoginVo.class));
            return true;
        }
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

接下来要把这个拦截器注册到配置当中。


2.6 创建WebMvcConfig

在config包下创建该类。这个类当中配置了两个拦截器,一个是登录拦截器,另一个是用户权限校验拦截器。用户校验拦截器下边再说。登录拦截器只需要部分接口进行拦截就可以了,毕竟有的接口不需要登陆用户就可以访问。

有一点值得注意的是,在这个配置类中配置的拦截器的顺序会影响校验结果,校验的流程是根据你配置的拦截器的顺序从上往下校验的,如果你把拦截器配置写反了就会出错。

addInterceptor注册一个拦截器
addPathPatterns该拦截器需要拦截的路径
excludePathPatterns该拦截器不需要拦截的路径
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {

    @Resource
    LoginInterceptor loginInterceptor;
    @Resource
    ActionInterceptor actionInterceptor;

    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(
                        "/test/**",
                        "/redis/**",
                        "/user/login",
                        "/user/logout/**",
                        "/category/all",
                        "/ebook/list",
                        "/doc/all/**",
                        "/doc/find-content/**",
                );

        registry.addInterceptor(actionInterceptor)
                .addPathPatterns(
                        "/*/save",
                        "/*/delete/**",
                        "/*/reset-password");
    }
}

2.7 用户权限校验拦截器

看到下方的代码你应该知道了用户上下文的作用,通过用户上下文拿到用户的信息来判断该用户是否有访问该接口的权利,我们拒绝非admin用户外的用户进行增删改操作。

但是这种方法有点不太好不知道你们有没有感觉到,一旦用户多了之后,如果你想给用户分配权限,你就要添加很多的用户在这里。所以一种更好的方式就是RBAC权限校验,大家可以自己了解一下,也有更好的权限校验框架SpringSecurity,但是作为一个比较简单的项目,引入这个框架的学习成本就太大了。

/**
 * 拦截器:Spring框架特有的,常用于登录校验,权限校验,请求日志打印
 */
@Component
public class ActionInterceptor implements HandlerInterceptor {

    private static final Logger LOG = LoggerFactory.getLogger(ActionInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        // OPTIONS请求不做校验,
        // 前后端分离的架构, 前端会发一个OPTIONS请求先做预检, 对预检请求不做校验
        if ("OPTIONS".equals(request.getMethod().toUpperCase())) {
            return true;
        }
        UserLoginVo userLoginResp = LoginUserContext.getUser();
        if ("admin".equals(userLoginResp.getLoginName())) {
            // admin用户不拦截
            return true;
        }
        LOG.info("操作被拦截");
        response.setStatus(HttpStatus.OK.value());
        CommonResp commonResp = new CommonResp(false,"普通用户暂不开放增删改操作",null);
        response.setContentType("application/json;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().print(JSONObject.toJSON(commonResp));
        return false;
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1992076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

职业本科大数据实训室

一、职业本科大数据实训室建设背景 在数字化浪潮汹涌澎湃的今天&#xff0c;大数据已跃升为引领社会进步和经济发展的新引擎。随着《中华人民共和国国民经济和社会发展第十四个五年规划和2035年远景目标纲要》的深入实施&#xff0c;数字化转型作为国家战略的重要组成部分&…

微信小程序登录获取 session_key 和 openid

一、申请测试小程序&#xff0c;只要微信扫码授权就可以申请了。 二、调用接口获取登录凭证&#xff08;code&#xff09;。通过凭证进而换取用户登录态信息&#xff0c;包括用户在当前小程序的唯一标识&#xff08;openid&#xff09;、微信开放平台账号下的唯一标识&#xf…

黔东南苗族文化展示小程序的设计与实现-计算机毕业设计源码85589

摘要 黔东南苗族文化作为中国传统文化的重要组成部分之一&#xff0c;具有悠久的历史和丰富的民俗传统。然而&#xff0c;随着社会的发展和现代化进程&#xff0c;苗族文化面临着传承和保护的挑战。为了更好地传播和展示黔东南苗族文化&#xff0c;本研究设计并实现了一款专注…

leetcode-119-杨辉三角II

原理&#xff1a; 1、初始化每行一维数组nums[1]&#xff1b; 2、从第2行开始&#xff0c;在nums的头插入0&#xff08;因为杨辉三角每行的第一个1相当于是上一行的1与其前面的0相加之和&#xff09;后进行相加操作。 代码&#xff1a;

MySQL——数据库的操作,数据类型,表的操作

MySQL——数据库的操作&#xff0c;数据类型&#xff0c;表的操作 1. 数据库的操作1.1 显示当前数据库1.2 创建数据库舍弃当前所写的SQL语句查看当前数据库服务全局的默认字符集 1.3 使用数据库1.4 查看当前操作的数据库查看MySQL的帮助 1.5 删除数据库 2. 常见数据类型2.1 数值…

Java生成Word->PDF->图片

文章目录 引言I Java生成Word、PDF、图片文档获取标签渲染数据生成文档案例II 工具类封装2.1 word 渲染和word 转 pfd2.2 pdf转成一张图片III poi-tl(word模板渲染) 标签简介文本标签{{var}}图片标签表格标签IV poi-tl提供了类 Configure 来配置常用的设置标签类型前后缀see al…

【Vue3】图片未加载成功前占位

背景 在写项目时&#xff0c;加载图片未成功前&#xff0c;会出现空白页面&#xff0c;太影响美观和体验感 解决方案 1. element ui通过slot占位符解决 2. 自定义指令 原生img标签可以通过自定义指令解决&#xff0c;img标签有onload和onerror事件&#xff0c;都是在渲染成…

svg封装使用

1、安装库 "vite-plugin-svg-icons": "^2.0.1" 2、配置svg vite.config中配置&#xff1a; 主要是配置createSvgIconsPlugin import react from vitejs/plugin-react import viteESLintPlugin from vite-plugin-eslint import { loadEnv } from vite im…

VLSI | 计算CMOS反相器的负载电容在BSIM4中的相关参数

ref. SPICE Model Parameters for BSIM4.5.0 (ubc.ca)PTM (umn.edu) 来自UMN的Microelectronics Co-design Research Group给出了晶体管PTM模型可以在SPICE仿真中使用&#xff1a;PTM (umn.edu)&#xff0c;但是由于使用Google才能下载&#xff0c;因此搬运到了这里&#xff…

电机制造业MES系统:直面行业痛点,引领智能化发展趋势

在电机制造业中&#xff0c;MES的应用具有重要意义。由于该行业的产品种类繁多&#xff0c;生产工艺复杂多变&#xff0c;生产现场的信息化管理难度较大。而通过引入MES&#xff0c;企业可以实现对生产现场的实时监控、生产进度的准确把握以及产品质量的有效控制。 电机行业信息…

什么是Docker | Docker入门及应用

1 Docker简介 1.1 什么是Docker Docker 是一个开源项目&#xff0c;诞生于 2013 年初&#xff0c;最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会&#xff0c;遵从了 Apache 2.0 协议&#xff0c;项目代码在 …

【ZooKeeper】ZooKeeper快速入门

1.ZooKeeper的概念 Zookeeper 是 Apache Hadoop 项目下的一个子项目&#xff0c;是一个树形目录服务。Zookeeper 翻译过来就是动物园管理员&#xff0c;它是用来管 Hadoop&#xff08;大象&#xff09;、Hive&#xff08;蜜蜂&#xff09;、Pig&#xff08;小猪&#xff09;的管…

CSAPP B站陪跑视频学习笔记

视频地址 How program run- overview 栈(stack) 栈的作用&#xff1a; 局部变量和参数&#xff1a;当函数被调用时&#xff0c;函数的局部变量和参数会被分配到栈上。这是因为这些变量的生命周期通常只限于函数执行期间。 函数调用链&#xff1a;栈用于管理函数调用链&#x…

Thinkphp5x远程执行命令及getshell

一.环境配置 靶场&#xff1a;vulhub/thinkphp/5-rce docker-compose up -d #启动环境 访问靶场&#xff1a; 漏洞利用&#xff1a; 漏洞根本源于 thinkphp/library/think/Request.php 中method⽅法可以进⾏变量覆盖&#xff0c;通 过覆盖类的核⼼属性filter导致rce&#xf…

手把手教你OpenCV实现Canny边缘检测 C++

1,原理 canny边缘检测算子是传统边缘检测算子中最优秀的&#xff0c;canny检测基于下面三个目标&#xff1a; &#xff08;1&#xff09;低错误率。即所有边缘都应该找到&#xff0c;并且没有虚假边缘。 &#xff08;2&#xff09;准确的定位边缘。即检测到的边缘应该接近真…

几款设计师必备的AI抠图软件工具分享给你!

前言 在图像处理领域&#xff0c;抠图是一项基本而关键的技能。传统上&#xff0c;PS是作为抠图的首选工具&#xff0c;但其操作复杂性往往令初学者望而却步。幸运的是&#xff0c;随着AIGC技术的发展&#xff0c;现在有多款AI软件和在线网站能够以更简单、快捷的方式完成抠图…

怎么把图片压缩更小?快捷缩小图片的3个在线工具

现在每天都会需要接触很多的图片&#xff0c;当图片在本地存储到一定数量时会占用大量的内存&#xff0c;并且不利于在网上传输使用&#xff0c;那么有什么方法能够快速压缩图片太大呢&#xff1f;在线改图片大小是一种比较简单的缩小图片的处理方法&#xff0c;在网上也有很多…

【多线程-从零开始-柒】代码案例1—单例模式

单例模式&#xff1a;是一种设计模式 设计模式&#xff0c;类似于“棋谱”&#xff0c;就是固定套路&#xff0c;针对一些特定的场景&#xff0c;给出一些比较好的解决方法只要按照设计模式来写代码&#xff0c;就可以保证代码不会太差&#xff0c;保证代码的下限 设计模式 设…

【性能优化】DNS解析优化

前言 DNS解析过程消耗时间DNS有本地缓存 比如首次访问某站点&#xff0c;会耗费很多时间进行DNS解析&#xff0c;但解析结束后会将ip地址存入本地设备&#xff0c;后续再访问此域名时就会直接从缓存中取。 首次访问页面时&#xff0c;本页面的DNS解析是无法优化的&#xff0…

antv l7简化版demo(含mapbox样例)

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><link rel"stylesheet" href"https://gw.alipayobjects.com/os/rmsportal/PqLCOJpqoOUfuPRacUzE.css" /><title>滑过默认高亮</…