手把手教你入门vue+springboot开发(三)--登录功能后端

news2025/2/24 18:56:34

文章目录

  • 前言
  • 一、redis安装
  • 二、后端代码
    • 1.修改application.yml文件
    • 2.增加utils文件
    • 3.增加Result类
    • 4.修改UserController类
    • 5.修改UserMapper类
    • 6.修改UserService和UserServiceImpl类
    • 7.增加LoginInterceptor类
    • 8.增加WebConfig类
    • 9.修改pom.xml文件


前言

前两篇我们用vue+springboot框架实现了一个很简单的查询数据的demo,通过这个demo主要是熟悉IDEAU和VSCode等工具的使用,以及熟悉vue+springboot前后端分离开发的基本流程。接下来两篇我们将在前面的基础上实现登录这个B/S典型应用场景,进一步深入的理解B/S架构、vue的一些基础插件、前后端交互过程。


一、redis安装

1)首先下载redis压缩包Redis-x64-3.0.504.zip,地址如下:
https://github.com/MicrosoftArchive/redis/releases
2)解压到本地路径C:\Program Files,进入解压后Redis-x64-3.0.504目录,执行以下命令:
redis-server.exe redis.windows.conf
如下图所示表示redis启动成功。
在这里插入图片描述
先把数据库中password改成md5加密,如下图,admin密码为:12345678,数据库中改成它的md5码:25d55ad283aa400af464c76d713c07ad
在这里插入图片描述

二、后端代码

1.修改application.yml文件

spring配置字段下增加以下内容连接redis服务器。

data:
    redis:
      host: localhost
      port: 6379

2.增加utils文件

增加utils包,里面增加JwtUtil、Md5Util、ThreadLocalUtil类,增加JWT、MD5和线程局部变量方法。
JwtUtil类代码:

package com.example.demo.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

import java.util.Date;
import java.util.Map;

public class JwtUtil {

    private static final String KEY = "itheima";

    //接收业务数据,生成token并返回
    public static String genToken(Map<String, Object> claims) {
        return JWT.create()
                .withClaim("claims", claims)
                .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 ))
                .sign(Algorithm.HMAC256(KEY));
    }

    //接收token,验证token,并返回业务数据
    public static Map<String, Object> parseToken(String token) {
        return JWT.require(Algorithm.HMAC256(KEY))
                .build()
                .verify(token)
                .getClaim("claims")
                .asMap();
    }

}

Md5Util类代码:

package com.example.demo.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Md5Util {
    /**
     * 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合
     */
    protected static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    protected static MessageDigest messagedigest = null;

    static {
        try {
            messagedigest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException nsaex) {
            System.err.println(Md5Util.class.getName() + "初始化失败,MessageDigest不支持MD5Util。");
            nsaex.printStackTrace();
        }
    }

    /**
     * 生成字符串的md5校验值
     *
     * @param s
     * @return
     */
    public static String getMD5String(String s) {
        return getMD5String(s.getBytes());
    }

    /**
     * 判断字符串的md5校验码是否与一个已知的md5码相匹配
     *
     * @param password  要校验的字符串
     * @param md5PwdStr 已知的md5校验码
     * @return
     */
    public static boolean checkPassword(String password, String md5PwdStr) {
        String s = getMD5String(password);
        return s.equals(md5PwdStr);
    }


    public static String getMD5String(byte[] bytes) {
        messagedigest.update(bytes);
        return bufferToHex(messagedigest.digest());
    }

    private static String bufferToHex(byte bytes[]) {
        return bufferToHex(bytes, 0, bytes.length);
    }

    private static String bufferToHex(byte bytes[], int m, int n) {
        StringBuffer stringbuffer = new StringBuffer(2 * n);
        int k = m + n;
        for (int l = m; l < k; l++) {
            appendHexPair(bytes[l], stringbuffer);
        }
        return stringbuffer.toString();
    }

    private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
        char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>>
        // 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同
        char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换
        stringbuffer.append(c0);
        stringbuffer.append(c1);
    }
}

ThreadLocalUtil类代码:

package com.example.demo.utils;

import java.util.HashMap;
import java.util.Map;

/**
 * ThreadLocal 工具类
 */
@SuppressWarnings("all")
public class ThreadLocalUtil {
    //提供ThreadLocal对象,
    private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();

    //根据键获取值
    public static <T> T get(){
        return (T) THREAD_LOCAL.get();
    }

    //存储键值对
    public static void set(Object value){
        THREAD_LOCAL.set(value);
    }


    //清除ThreadLocal 防止内存泄漏
    public static void remove(){
        THREAD_LOCAL.remove();
    }
}

3.增加Result类

bean增加Result类,定义HTTP返回结果格式。

package com.example.demo.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//统一响应结果
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {
    private Integer code;//业务状态码  0-成功  1-失败
    private String message;//提示信息
    private T data;//响应数据

    //快速返回操作成功响应结果(带响应数据)
    public static <E> Result<E> success(E data) {
        return new Result<>(0, "操作成功", data);
    }

    //快速返回操作成功响应结果
    public static Result success() {
        return new Result(0, "操作成功", null);
    }

    public static Result error(String message) {
        return new Result(1, message, null);
    }
}

4.修改UserController类

selectAll方法的返回值需要使用Result定义格式,参照下面代码改一下,然后增加login方法处理"/user/login"请求,校验用户名、密码后生成JWT令牌,并保存在redis中,当其它请求过来时需要验证JWT令牌,同时令牌中还保存了用户名和密码等信息,当需要这些信息时就不用再查询mysql数据库。

 @Autowired
    private StringRedisTemplate stringRedisTemplate;

   @PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password) {
        //根据用户名查询用户
        User loginUser = userService.findByUserName(username);
        //判断该用户是否存在
        if (loginUser == null) {
            return Result.error("用户名错误");
        }

        //判断密码是否正确  loginUser对象中的password是密文
        if (Md5Util.getMD5String(password).equals(loginUser.getPassword())) {
            //登录成功
            Map<String, Object> claims = new HashMap<>();
            claims.put("id", loginUser.getId());
            claims.put("username", loginUser.getUserName());
            String token = JwtUtil.genToken(claims);
            //把token存储到redis中
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            operations.set(token,token,1, TimeUnit.HOURS);
            return Result.success(token);
        }
        return Result.error("密码错误");
    }

增加后有些字段会是红色,工具支持自动导入相关的类,鼠标放在字段上会出现下图提示,选择“Import class”即可。
在这里插入图片描述
注意Pattern字段工具自动导入的类是
import org.intellij.lang.annotations.Pattern;
这里regexp可能识别不到,那就换成下面的类,但需要增加spring-boot-starter-validation依赖
import jakarta.validation.constraints.Pattern;

5.修改UserMapper类

增加findByUserName方法。

    //根据用户名查询用户
    @Select("select * from users where userName=#{username}")
    User findByUserName(String username);

6.修改UserService和UserServiceImpl类

UserService类增加:

    //根据用户名查询用户
    User findByUserName(String username);

UserServiceImpl类增加:

    @Override
    public User findByUserName(String username) {
        User u = userMapper.findByUserName(username);
        return u;
    }

7.增加LoginInterceptor类

增加interceptors包,里面增加LoginInterceptor类,这里主要通过拦截器处理校验令牌的过程。

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //处理OPTIONS请求时,返回正确的CORS头部信息
        if (request.getMethod().equals("OPTIONS")) {
            response.setHeader("Access-Control-Allow-Origin", "*");//*表示放行所有的源
            response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, HEAD, OPTIONS");
            response.setHeader("Access-Control-Allow-Headers", "*");
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setStatus(HttpServletResponse.SC_OK);
            return false;
        }
        else {
            //令牌验证
            String token = request.getHeader("Authorization");
            //验证token
            try {
                //从redis中获取相同的token
                ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
                String redisToken = operations.get(token);
                if (redisToken == null) {
                    //token已经失效了
                    throw new RuntimeException();
                }
                Map<String, Object> claims = JwtUtil.parseToken(token);

                //把业务数据存储到ThreadLocal中
                ThreadLocalUtil.set(claims);
                //放行
                return true;
            } catch (Exception e) {
                //http响应状态码为401
                response.setStatus(401);
                //不放行
                return false;
            }
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //清空ThreadLocal中的数据
        ThreadLocalUtil.remove();
    }
}

这里调试的时候出现跨域访问不了后端/user/selectAll的情况,所以需要加上OPTIONS这段代码。

8.增加WebConfig类

增加config包,里面增加WebConfig类,如果不需要拦截可以在这里设置。

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //登录接口不拦截
        registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login");
    }

    //解决跨域问题
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOriginPatterns("*")
                .allowedMethods("GET", "HEAD", "POST","PUT", "DELETE", "OPTIONS")
                .allowCredentials(true).maxAge(3600);
    }
}

解决跨域问题可以直接在这里增加addCorsMappings方法,这样就不需要在每个controller类中增加@CrossOrigin。

9.修改pom.xml文件

增加上面代码需要的依赖。

<!--redis坐标-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
<!--lombok依赖-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
<!--java-jwt坐标-->
    <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>4.4.0</version>
    </dependency>
<!--validation依赖-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>

下一篇我们继续实现前端代码

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

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

相关文章

内网不能访问网站怎么办?

内网不能访问网站是在网络使用过程中常见的问题之一。当我们使用局域网连接时&#xff0c;有时候会遇到无法访问特定网站的情况。这可能是因为网络环境复杂&#xff0c;或者受到了某些限制。本篇文章将介绍一种解决内网不能访问网站问题的产品——天联组网。 天联组网是一款由…

非计算机专业可以考“软考”吗?

全国计算机软件水平考试对报名条件没有学历、资历、年龄以及专业等限制&#xff0c;非计算机专业的人员也可以报考。证书长期有效&#xff0c;考生可根据个人需求选择合适的级别和资格进行报考。报名方式包括网上报名和考生本人到指定地点报名两种。 考试范围 (1) 高级资格包括…

RS485和CAN电路中的TVS管选择

在RS485和CAN电路设计中&#xff0c;经常要考虑“静电和浪涌保护”&#xff0c;怎么选择TVS管&#xff0c;很少有人讲解。 1、先了解TVS管 TVS管有单向管和双向管&#xff0c;通常后缀为CA的是双向TVS管&#xff0c;只有字母A的是单向TVS管。见下图&#xff1a; 2、TVS选择依…

C++11列表初始化{}

列表初始化 C11后为了能让自定义类型也能够快速被初始化新增 {} 内置类型变量 int a1 { 10 };int a2{ 11 };int a3 { 1 2 };int a4{ 1 2 }; 注意&#xff1a;列表初始化可以在{}之前使用等号&#xff0c;其效果与不使用没有什么区别。 内置类型数组 int arr1[] { 1,2,3…

安装前端依赖node-sass报错

文章目录 问题1&#xff1a;node-sass报错问题2&#xff1a;node-gyp报错问题3&#xff1a;node-sass再次报错问题4&#xff1a;node-sass三次报错 问题1&#xff1a;node-sass报错 问题描述&#xff1a;经常会碰到一个新的项目安装依赖时&#xff0c;会报node-sass版本的问题…

白嫖Cloudflare Workers 搭建 Docker Hub镜像加速服务

简介 基于Cloudflare Workers 搭建 Docker Hub镜像加速服务。 首先要注册一个Cloudflare账号。 Cloudflare账号下域名的一级域名&#xff0c;推荐万网注册个top域名&#xff0c;再转移到Cloudflare&#xff0c;很便宜的。 注意 Worker 每天每免费账号有次数限制&#xff0c;…

文件简单做二维码的方法,几步就能够完成操作

怎样用二维码来查看文件内容&#xff1f;随着网络的快速发展&#xff0c;通过二维码来查看文件是现在很常用的一种形式&#xff0c;能够更快让其他人获取文件内容&#xff0c;从而提升传播的速度和效率。比如用这种方式来下发通知文件、分享学习资料、浏览海报图片、传递个人简…

[Java基本语法] 数组及其应用

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;线程与…

zookeeper介绍 和 编译踩坑

zookeeper 分布式协调服务 ZooKeeper原理及介绍 - 鹿泉 - 博客园 Zookeeper是在分布式环境中应用非常广泛&#xff0c;它的优秀功能很多&#xff0c;比如分布式环境中全局命名服务&#xff0c;服务注册中心&#xff0c;全局分布式锁等等。 本项目使用其分布式服务配置中心&am…

Qt 非圆角图片裁剪为圆角图片

将Qt非圆角图片裁剪为圆角图片,步骤如下&#xff1a; 1、按照原始图片尺寸定义一张透明的新图形 2、使用画家工具在新图形上绘制一个圆角矩形线路 3、绘制图片 4、使用圆角矩形切割图片边角 封装成函数如下&#xff1a; QPixmap Widget::getRoundedPixmap(const QPixmap srcPix…

注解 - @ControllerAdvice

注解简介 在今天的每日一注解中&#xff0c;我们将探讨ControllerAdvice注解。ControllerAdvice是Spring框架中的一个注解&#xff0c;用于集中处理应用程序中所有控制器的全局异常处理、数据绑定和数据预处理。 注解定义 ControllerAdvice注解用于定义一个全局的异常处理、数…

PyQt5学习系列之新项目创建并使用widget

PyQt5学习系列之新项目创建并使用widget 前言报错新建项目程序完整程序总结 前言 新建项目&#xff0c;再使用ui转py&#xff0c;无论怎么样都打不开py文件&#xff0c;直接报错。 报错 Connected to pydev debugger (build 233.11799.298)新建项目程序 # Press ShiftF10 to…

[C++][数据结构][二叉搜索树]详细讲解

目录 1.概念2.二叉搜索树操作1.查找2.插入3.删除 3.二叉搜索树的实现4.二叉搜索树的应用1.K模型2.KV模型 5.二叉搜索树的性能分析 1.概念 二叉搜索树又称二叉排序树&#xff0c;具有以下性质 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子…

深入剖析云原生服务与微服务框架中服务发现机制的核心原理与实现细节

深入剖析云原生服务与微服务框架中服务发现机制的核心原理与实现细节 本文介绍单体架构微服务架构微服务的方法建议微服务的通信机制P2P的调用通信API网关统一门面 微服务服务发现DNS服务发现基于一致性原则的key-value数据库服务发现与负载均衡紧密相关 微服务发展到容器化云原…

如何警用root用户登录ssh

使用tail指令&#xff0c;可以动态查看日志信息。 &#xff08;tail -f /var/log/secure或messages&#xff09; 使用>符号&#xff0c;可以清空日志内容&#xff0c;不删除文件本身。 禁用root用户为以下步骤&#xff1a; 首先使用useradd创建用户&#xff08;可以修改为其…

PyTorch C++扩展用于AMD GPU

PyTorch C Extension on AMD GPU — ROCm Blogs 本文演示了如何使用PyTorch C扩展&#xff0c;并通过示例讨论了它相对于常规PyTorch模块的优势。实验在AMD GPU和ROCm 5.7.0软件上进行。有关支持的GPU和操作系统的更多信息&#xff0c;请参阅系统要求&#xff08;Linux&#xf…

Vue页面内容未保存时离开页面做弹框提示

一、背景 目标&#xff1a;如果当前页面中有正在编辑中的内容&#xff0c;那么此时切换组件、跳转路由、关闭标签页、刷新页面&#xff0c;都会有离开确认提示&#xff0c;防止用户因误触导致填写的内容白费。 后台管理系统中有许多需要填写的表单&#xff0c;弹窗方式的表单一…

Docker安装Nginx(各种错误版)

Docker安装-CSDN博客 安装启动Docker之后 docker run -d -p 81:81 --name nginx nginx 这样没有指定版本 docker run&#xff1a;启动一个新的容器。-d&#xff1a;以分离模式运行容器&#xff08;后台运行&#xff09;。-p 81:81&#xff1a;将主机的 81 端口映射到容器的 …

“论面向对象的建模及应用”必过范文,突击2024软考高项论文

论文真题 软件系统建模是软件开发中的重要环节&#xff0c;通过构建软件系统模型可以帮助系统开发人员理解系统&#xff0c;抽取业务过程和管理系统的复杂性&#xff0c;也可以方便各类人员之间的交流。软件系统建模是在系统需求分析和系统实现之间架起的一座桥梁&#xff0c;…

傲星一个在线工具箱源码附搭建教程

傲星工具箱源码是一款功能强大的在线工具箱程序&#xff0c;您可以通过安装扩展来增强其功能。同时&#xff0c;该程序还提供了插件模板的功能&#xff0c;让您可以将其作为网页导航使用。 1.PHP版本需不低于7.2.5。 2.Mysql版本需不低于5.7。 3.需要安装fileinfo扩展。 4.…