当前位置: 首页 > article >正文

Springboot项目搭建(1)-用户登录与注册

1.引入lombok依赖

 若<dependency>中数据为红,则说明Maven本地仓库里未引用依赖

可在右侧“m”标识中,下载源代码和文档后刷新。

2.统一响应数据Result

在entity文档下创建,名为Result的java类

文件地址:org/example/entity/Result.java

package org.example.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//创建统一响应数据的类
@NoArgsConstructor //创建无参数构造器
@AllArgsConstructor //创建有参数构造器
@Data //配置访问属性的方法(get/set)
public class Result<T>{
    private Integer code; //该属性用于存放响应码
    private String message; //提示信息
    private T data; //响应数据,T表示任意类型
    // 创建一个方法,用于返回操作成功的反应结果,
    // 如:查询个人信息,返回操作成功提示及个人信息数据
    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);
    }
}

创建一个方法:

public static <E> Result <E> success(E data){
        return new Result<>(0,"操作成功",data);

  • public 表示方法的访问权限
  • static 静态只被创建一次,配置该方法存放内存中的方法区
  • <E> 方法的返回类型,E表示任意类型
  • result<E>返回类型,是当前类的类型
  • success方法名称
  • (E data)表示参数列表,调用方法时可以通过参数列表向方法传递数据
  • return 方法返回数据关键字
  • new Result<>() 表示创建对象,创建一个Result对象,
  • 创建对象时传入三个数据,分别是状态码,提示信息以及携带的数据

3. 创建文档

4. 数据格式化User

org/example/entity/User.java路径中,

定义了一个用于存储和传输用户信息的Java实体类。

package org.example.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;
    private String nickname;
    private String userPic;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

5. 数据库交互UserMapper

声明当前文件为映射文件,与数据库进行数据交互的文件

这个 UserMapper 接口提供了两个基本的数据库操作:添加用户和根据用户名查询用户。

通过MyBatis框架,这些方法可以被Spring框架自动调用,从而实现数据的持久化操作。

这种方式使得数据库操作更加简洁和面向对象。

package org.example.mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.example.entity.User;
@Mapper
public interface UserMapper {
    //添加用户信息
    @Insert("insert into user(username,password,create_time,update_time)" +
    "values(#{username},#{password},now(),now())")
    void insertUser(String username, String password);
    //根据用户名查询用户信息
    @Select("select * from user where username=#{username}")
    User selectUserByUsername(String username);
}

6.  加密和密码验证

6.1 数据加密 Md5Util

org/example/utils/Md5Util.java中,定义了一个工具类 Md5Util

  • 生成MD5哈希值:将输入的字符串转换为MD5哈希值。
  • 验证密码:检查输入的密码是否与给定的MD5哈希值相同。
  • 将字节数组转换为十六进制字符串:用于表示MD5哈希值的结果。

以下代码来源于网络,有志者也可自行书写 。

package org.example.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);
    }
}

6.2 密文查看与验证 Md5Test

首先在pom.xml中:导入Spring提供的测试工具

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

 而后创建新的测试文件:src/test/java/Md5Test.java

可查看“123456(为例)”转码后的结果。

import org.example.utils.Md5Util;
import org.junit.jupiter.api.Test;
import java.sql.SQLOutput;
public class Md5Test {
    @Test
    public void test(){
        String md5Password = Md5Util.getMD5String("123456");
        System.out.println(md5Password);
    }
}

7. 定义用户基操规UserService

7.1 创建接口

文件地址:org/example/service/UserService.java

package org.example.service;
import org.example.entity.User;
public interface UserService {
    //根据用户名查询用户信息
    User findUserByUsername(String username);
    //注册
    void register(String username, String password);
}

7.2 实现接口<lmpl>

文件地址:org/example/service/impl/UserServiceImpl.java

补充UserService.java的具体操作过程

package org.example.service.impl;
import org.example.entity.User;
import org.example.mapper.UserMapper;
import org.example.service.UserService;
import org.example.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//声明当前类为业务类,在业务类下可以调用事务相关配置
@Service
public class UserServiceImpl implements UserService {
    //调用UserMapper操作数据库
    @Autowired
    @SuppressWarnings("all") //压制警告
    private UserMapper userMapper;
    @Override
    public User findUserByUsername(String username) {
        return userMapper.selectUserByUsername(username);
    }
    @Override
    public void register(String username, String password) {
        //加密
        String md5String = Md5Util.getMD5String(password);
        userMapper.insertUser(username, md5String);
    }
}

小结:UserServiceImplUserService 接口的具体实现,它提供了接口中声明的方法的实际业务逻辑,并通过依赖注入使用 UserMapper 与数据库进行交互。这种设计模式使得代码结构清晰,易于维护和扩展。

8. 注册UserController

首先在pom.xml中添加:用于请求参数验证(校验)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

org/example/controller/UserController.java中建立一个UserController 作为表现层,

它的Spring Boot控制器类,负责处理与用户相关的HTTP请求和响应

package org.example.controller;
import org.example.entity.Result;
import org.example.entity.User;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController //声明该类是一个控制器类,用于浏览器与该类数据交互
@RequestMapping("/user") //声明该类的访问地址
@Validated //声明该类中的方法支持参数校验
public class UserController {
    @Autowired
    @SuppressWarnings("all")
    private UserService userService;
    //注册
    @PostMapping("/register")
    public Result register(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password) {
        //查询注册的用户名是否存在
        User user = userService.findUserByUsername(username);
        //判断查询的用户是否存在,如果不存在则返回空
        if (user == null) {
            //该用户名可以使用
            userService.register(username, password);
            //返回操作成功
            return Result.success();
        }else{
            //该用户名不允许使用
            return Result.error("用户名已被占用");
        }
    }

@Pattern(regexp = "^\\S{5,16}$")String username:

  • @Pattern:表示该参数使用校验规则,regexp表示使用正则表达式的校验规则
  •  ^:表示正则表达式的开始
  •  $:表示正则表达式的结束
  •  \\:转译字符,转译后为\
  •  \S:表示任意字符
  •  {5,16}:字符的位数在5~16间

实验结果:

9. 全局异常类GlobalExceptionHandle

创建org/example/exception/GlobalExceptionHandle.java文件,统一处理异常报告。

package org.example.exception;
import org.example.entity.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandle {
    //统一返回异常信息
    @ExceptionHandler(value = Exception.class)
    public Result handleException(Exception e) {
        return Result.error(e.getMessage());
    }
}

10. 登录UserController

org/example/controller/UserController.java中的注册代码下方,续写登录代码。

循环逻辑:如果上面的if体未执行,则表示该用户存在,由于查询的是用户所有信息,包含数据库中的密码,下面的判断为判断前端传入的密码,与数据库的密码是否相等

package org.example.controller;
import jakarta.validation.constraints.Pattern;
import org.example.entity.Result;
import org.example.entity.User;
import org.example.service.UserService;
import org.example.utils.JwtUtil;
import org.example.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
@Validated
public class UserController {
    @Autowired
    @SuppressWarnings("all")
    private UserService userService;
    //注册
    @PostMapping("/register")
    public Result register(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password) {...}
    //登录
    @PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password){
        //根据用户名查询用户信息
        User loginUser = userService.findUserByUsername(username);
        //如果没有用户信息则返回登录失败提示
        if(loginUser == null){
            return Result.error("用户名或密码错误");
        }
        if (Md5Util.getMD5String(password).equals(loginUser.getPassword())){
            return Result.success("登录成功");
        }
        return Result.error("登录失败,用户名或密码错误");
    }
}

实验结果:

11. JWT令牌

11.1 认识JWT令牌

全称:JSON Web Token,由Header、Payload、Signature三部分组成:

Header(头):记录令牌类型,参数算法等;

Payload(荷载):携带用户信息等一些自定义的默认信息;

Signature(签名):防止信息被篡改,而设定的算法逻辑密文。

11.2 引入令牌 JwtUtil

添加至org/example/utils/JwtUtil.java

package org.example.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 = "usermessage";
	//接收业务数据,生成token并返回
    public static String genToken(Map<String, Object> claims) {
        return JWT.create()
                .withClaim("claims", claims)
                .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
                .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();
    }
}

引入后,在pom.xml中添加:jwt依赖(令牌技术)

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.4.0</version>
</dependency>

11.3 测试令牌 JwtTest

在test/java下,建立src/test/java/JwtTest.java测试文件:

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.junit.jupiter.api.Test;

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

public class JwtTest {

    @Test
    public void testJwt() {
        //创建容器(集合),用于存放令牌(用户)数据
        Map<String,Object> claims = new HashMap<>();
        claims.put("id",1);
        claims.put("name","Hela");
        //生成Jwt
        String token = JWT.create() //创建令牌
                .withClaim("userwagaga",claims) //载荷数据
                .withExpiresAt(new Date(System.currentTimeMillis()*1000*60*60*12)) //过期时间(生命周期)
                .sign(Algorithm.HMAC256("usermessage")); //指定算法,配置加密密钥
        System.out.println(token);
    }
    @Test
    public void testParse(){
        String token ="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
                "eyJ1c2Vyd2FnYWdhIjp7Im5hbWUiOiJIZWxhIiwiaWQiOjF9LCJleHAiOjEwMzU0MTc4NDQ2MDY1OTN9." +
                "tbhdeg5kp1u8Hp7NBxT8ALSaAW9-iBLasj_AdIBAYpQ";
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("usermessage")).build();
        DecodedJWT decodedJWT= jwtVerifier.verify(token);
        Map<String, Claim> claimMap = decodedJWT.getClaims();
        System.out.println(claimMap.get("userwagaga"));
    }
}

11.4 将令牌补充至登录代码中

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

@PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password){
        //根据用户名查询用户信息
        User loginUser = userService.findUserByUsername(username);
        //如果没有用户信息则返回登录失败提示
        if(loginUser == null){
            return Result.error("用户名或密码错误");
        }
        if (Md5Util.getMD5String(password).equals(loginUser.getPassword())){
            Map<String,Object> claims = new HashMap<>();// 存放用户令牌数据的容器(集合)
            claims.put("id",loginUser.getId());
            claims.put("username",loginUser.getUsername());
            claims.put("password",loginUser.getPassword());
            String token = JwtUtil.genToken(claims);
            return Result.success(token);
        }
        return Result.error("登录失败,用户名或密码错误");
    }

此阶段UserController完整代码:

package org.example.controller;

import jakarta.validation.constraints.Pattern;
import org.example.entity.Result;
import org.example.entity.User;
import org.example.service.UserService;
import org.example.utils.JwtUtil;
import org.example.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController //声明该类是一个控制器类,用于浏览器与该类数据交互
@RequestMapping("/user") //声明该类的访问地址
@Validated //声明该类中的方法支持参数校验
public class UserController {
    @Autowired
    @SuppressWarnings("all")
    private UserService userService;
    //注册
    @PostMapping("/register")
    public Result register(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password) {
        //查询注册的用户名是否存在
        User user = userService.findUserByUsername(username);
        //判断查询的用户是否存在,如果不存在则返回空
        if (user == null) {
            //该用户名可以使用
            userService.register(username, password);
            //返回操作成功
            return Result.success();
        }else{
            //该用户名不允许使用
            return Result.error("用户名已被占用");
        }
    }
    @PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password){
        //根据用户名查询用户信息
        User loginUser = userService.findUserByUsername(username);
        //如果没有用户信息则返回登录失败提示
        if(loginUser == null){
            return Result.error("用户名或密码错误");
        }
        if (Md5Util.getMD5String(password).equals(loginUser.getPassword())){
            Map<String,Object> claims = new HashMap<>();// 存放用户令牌数据的容器(集合)
            claims.put("id",loginUser.getId());
            claims.put("username",loginUser.getUsername());
            claims.put("password",loginUser.getPassword());
            String token = JwtUtil.genToken(claims);
            return Result.success(token);
        }
        return Result.error("登录失败,用户名或密码错误");
    }
}


http://www.kler.cn/a/401279.html

相关文章:

  • Linux网络:HTTPS协议
  • Springboot3.3.5 启动流程之 tomcat启动流程介绍
  • 一文了解 inductive bias(归纳偏好)
  • urdf笔记
  • Go语言基本类型转换
  • 十二:HTTP错误响应码:理解与应对
  • JavaScript 高级—求数组的最大值与最小值
  • llm模型训练导出部署一条龙
  • django——创建 Django 项目和 APP
  • STM32(hal库)中,__HAL_LINKDMA 函数使用时候,串口的handler DMA_HandleTypedef 为什么前面要加extern
  • 网络协议(4)拥塞控制
  • jvm原理介绍
  • vue-office:word(.docx)、pdf、excel(.xlsx,.xls)格式文件预览
  • 【FL0021】基于SpringBoot和微信小程序的高校就业招聘系统
  • 私域流量与视频号直播的融合创新:以 2+1 链动模式 S2B2C 商城小程序为例
  • 大型语言模型综述 A Survey of Large Language Models
  • M3-拟时序分许-3. 数据预处理、对齐和降维
  • 2024华为java面经
  • 光伏电站的方案PPT总结
  • .NET 9.0 LINQ 完全指南:从基础到高级应用场景
  • Excel表数学于三角函数、统计函数
  • 交换排序——快速排序3 针对LeetCode某OJ的优化
  • 基于ToLua的C#和Lua内存共享方案保姆级教程
  • STM32 的 DCMI 接口与 ESP32 的 DVP 接口的区别与作用
  • 在Ubuntu上部署Open WebUI和Ollama,打造你的私人GPT
  • LabVIEW 2024 安装教程